// import com.alibaba.druid.DbType;
// import com.alibaba.druid.sql.ast.*;
// import com.alibaba.druid.sql.ast.expr.*;
// import com.alibaba.druid.sql.ast.statement.*;
// import com.alibaba.druid.sql.dialect.oracle.ast.clause->OracleReturningClause;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt->*;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt->OracleAlterTableSplitPartition.NestedTablePartitionSpec;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt->OracleAlterTableSplitPartition.TableSpaceItem;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt->OracleAlterTableSplitPartition.UpdateIndexesClause;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt->OracleLockTableStatement.LockMode;
// import com.alibaba.druid.sql.parser.*;
// import com.alibaba.druid.util.FnvHash;

// import java.util.ArrayList;
// import java.util.List;

// import static com.alibaba.druid.sql.parser.Token::TABLE;

#include "OracleStatementParser.h"
#include "../ast/clause/OracleReturningClause.h"
#include "../ast/stmt/OracleAlterTableSplitPartition.h"
#include "../ast/stmt/OracleLockTableStatement.h"
#include "../ast/stmt/OracleInsertStatement.h"
#include "../ast/stmt/OracleExceptionStatement.h"
#include "../ast/stmt/OracleExitStatement.h"
#include "../ast/stmt/OracleContinueStatement.h"
#include "../ast/stmt/OracleExplainStatement.h"
#include "../ast/stmt/OracleForStatement.h"
#include "../ast/stmt/OracleLabelStatement.h"
#include "../ast/stmt/OracleGotoStatement.h"
#include "../ast/stmt/OracleDropDbLinkStatement.h"
#include "../ast/stmt/OracleRunStatement.h"
#include "../ast/stmt/OraclePipeRowStatement.h"
#include "../ast/stmt/OracleExecuteImmediateStatement.h"
#include "../ast/stmt/OracleRaiseStatement.h"
#include "../ast/stmt/OracleSetTransactionStatement.h"
#include "../ast/stmt/OracleAlterSessionStatement.h"
#include "../ast/stmt/OracleAlterTriggerStatement.h"
#include "../ast/stmt/OracleAlterSynonymStatement.h"
#include "../ast/stmt/OracleAlterViewStatement.h"
#include "../ast/stmt/OracleAlterTablespaceStatement.h"
#include "../ast/stmt/OracleAlterTablespaceAddDataFile.h"
#include "../ast/stmt/OracleFileSpecification.h"
#include "../ast/stmt/OracleAlterSummaryStatement.h"
#include "../ast/stmt/OracleAlterTableMoveTablespace.h"
#include "../ast/stmt/OracleConstraint.h"
#include "../ast/stmt/OracleAlterTableModify.h"
#include "../ast/stmt/OracleAlterTableTruncatePartition.h"
#include "../ast/stmt/OracleAlterTableRowMovement.h"
#include "../ast/stmt/OracleAlterTableShrinkSpace.h"
#include "../ast/stmt/OracleAlterTableDropPartition.h"
#include "../ast/stmt/OracleCreateTypeStatement.h"
#include "../ast/stmt/OracleMultiInsertStatement.h"
#include "../ast/stmt/OracleDeleteStatement.h"
#include "../ast/stmt/OracleCreateDatabaseDbLinkStatement.h"
#include "../ast/stmt/OracleCreateIndexStatement.h"
#include "../ast/stmt/OracleCreatePackageStatement.h"
#include "../ast/stmt/OracleCreateSynonymStatement.h"
#include "../../../../utils/FnvHash.h"
#include "../../../../utils/instanceof.h"
#include "../../../../utils/StringUtils.h"
#include "OracleExprParser.h"
#include "OracleCreateTableParser.h"
#include "OracleSelectParser.h"
#include "OracleUpdateParser.h"
#include "OracleExprParser.h"
#include "OracleFunctionDataType.h"
#include "OracleProcedureDataType.h"
#include "../../../ast/statement/SQLSelectStatement.h"
#include "../../../ast/statement/SQLUpdateStatement.h"
#include "../../../ast/statement/SQLDeleteStatement.h"
#include "../../../ast/statement/SQLScriptCommitStatement.h"
#include "../../../ast/statement/SQLCallStatement.h"
#include "../../../ast/statement/SQLSetStatement.h"
#include "../../../ast/statement/SQLBlockStatement.h"
#include "../../../ast/statement/SQLFetchStatement.h"
#include "../../../ast/statement/SQLExplainStatement.h"
#include "../../../ast/statement/SQLAnalyzeTableStatement.h"
#include "../../../ast/statement/SQLShowErrorsStatement.h"
#include "../../../ast/statement/SQLExprStatement.h"
#include "../../../ast/statement/SQLCommitStatement.h"
#include "../../../ast/statement/SQLSavePointStatement.h"
#include "../../../ast/statement/SQLDropTableStatement.h"
#include "../../../ast/statement/SQLDropViewStatement.h"
#include "../../../ast/statement/SQLDropSequenceStatement.h"
#include "../../../ast/statement/SQLDropUserStatement.h"
#include "../../../ast/statement/SQLDropTriggerStatement.h"
#include "../../../ast/statement/SQLDropProcedureStatement.h"
#include "../../../ast/statement/SQLOpenStatement.h"
#include "../../../ast/statement/SQLCreateProcedureStatement.h"
#include "../../../ast/statement/SQLDropTypeStatement.h"
#include "../../../ast/statement/SQLDropMaterializedViewStatement.h"
#include "../../../ast/statement/SQLDropSynonymStatement.h"
#include "../../../ast/statement/SQLAlterTableStatement.h"
#include "../../../ast/statement/SQLAlterTableRename.h"
#include "../../../ast/statement/SQLAlterTableItem.h"
#include "../../../ast/statement/SQLReturnStatement.h"
#include "../../../ast/statement/SQLWhileStatement.h"
#include "../../../ast/statement/SQLCreateFunctionStatement.h"
#include "../../../ast/statement/SQLIfStatement.h"
#include "../../../ast/statement/SQLLoopStatement.h"
#include "../../../ast/statement/SQLAlterProcedureStatement.h"
#include "../../../ast/statement/SQLAlterTypeStatement.h"
#include "../../../ast/statement/SQLAlterFunctionStatement.h"
#include "../../../ast/statement/SQLAlterTableAddColumn.h"
#include "../../../ast/statement/SQLAlterTableAddConstraint.h"
#include "../../../ast/statement/SQLAlterTableAddSupplemental.h"
#include "../../../ast/statement/SQLAlterTableDisableConstraint.h"
#include "../../../ast/statement/SQLAlterTableEnableConstraint.h"
#include "../../../ast/statement/SQLAlterTableDropConstraint.h"
#include "../../../ast/statement/SQLAlterTableDropColumnItem.h"
#include "../../../ast/statement/SQLAlterTableDropIndex.h"
#include "../../../ast/statement/SQLAlterTableDropPrimaryKey.h"
#include "../../../ast/statement/SQLCreateSequenceStatement.h"
#include "../../../ast/statement/SQLDeclareStatement.h"
#include "../../../ast/statement/SQLAlterSequenceStatement.h"
#include "../../../ast/statement/SQLGrantStatement.h"
#include "../../../ast/statement/SQLRevokeStatement.h"
#include "../../../ast/statement/SQLCommentStatement.h"
#include "../../../ast/statement/SQLColumnConstraint.h"
#include "../../../ast/expr/SQLBinaryOperator.h"
#include "../../../ast/expr/SQLCaseStatement.h"
#include "../../../ast/SQLStatement.h"
#include "../../../ast/SQLArgument.h"
#include "../../../ast/SQLParameter.h"
#include "../../../ast/SQLPartition.h"
#include "../../../ast/SQLPartitionByRange.h"
#include "../../../ast/SQLPartitionByHash.h"
#include "../../../ast/SQLRecordDataType.h"
#include "../../../ast/SQLDataTypeImpl.h"
#include "../../../ast/SQLDeclareItem.h"
#include "../../../parser/SQLExprParser.h"
#include "../../../parser/LayoutCharacters.h"

OracleStatementParser::OracleStatementParser(string_ptr sql)
    : SQLStatementParser(OracleExprParser_ptr(new OracleExprParser(sql)))
{
}

OracleStatementParser::OracleStatementParser(string_ptr sql, SQLParserFeature_list_ptr features)
    : SQLStatementParser(OracleExprParser_ptr(new OracleExprParser(sql, features)))
{
}

OracleStatementParser::OracleStatementParser(Lexer_ptr lexer)
    : SQLStatementParser(OracleExprParser_ptr(new OracleExprParser(lexer)))
{
}

// @Override
OracleExprParser_ptr OracleStatementParser::getExprParser()
{
  return std::dynamic_pointer_cast<OracleExprParser>(exprParser);
}

OracleCreateTableParser_ptr OracleStatementParser::getSQLCreateTableParser()
{
  return OracleCreateTableParser_ptr(new OracleCreateTableParser(lexer));
}

void OracleStatementParser::parseInsert0_hinits(SQLInsertInto_ptr insertStatement)
{
  if (instanceof <SQLInsertInto, OracleInsertStatement>(insertStatement))
  {
    OracleInsertStatement_ptr stmt = std::dynamic_pointer_cast<OracleInsertStatement>(insertStatement);
    this->getExprParser()->parseHints(stmt->getHints());
  }
  else
  {
    SQLHint_list_ptr hints = std::make_shared<std::list<SQLHint_ptr>>();
    ;
    this->getExprParser()->parseHints(hints);
  }
}

void OracleStatementParser::parseStatementList(SQLStatement_list_ptr statementList, int max, SQLObject_ptr parent)
{
  for (;;)
  {
    if (max != -1)
    {
      if (statementList->size() >= max)
      {
        return;
      }
    }

    if (lexer->token()->name->c_str() == Token::XEOF.name->c_str())
    {
      return;
    }
    if (lexer->token()->name->c_str() == Token::END.name->c_str())
    {
      return;
    }
    if (lexer->token()->name->c_str() == Token::ELSE.name->c_str())
    {
      return;
    }

    if (lexer->token()->name->c_str() == (Token::SEMI.name->c_str()))
    {
      lexer->nextToken();
      if (statementList->size() > 0)
      {
        auto it_statementList = statementList->begin();
        std::advance(it_statementList, statementList->size() - 1);
        SQLStatement_ptr lastStmt = *it_statementList;
        lastStmt->setAfterSemi(BOOL::TRUE);
      }
      continue;
    }

    if (lexer->token()->name->c_str() == (Token::SELECT.name->c_str()))
    {
      SQLStatement_ptr stmt = SQLSelectStatement_ptr(new SQLSelectStatement(OracleSelectParser_ptr(new OracleSelectParser(this->exprParser))->select(), DbType_ptr(new DbType(&DbType::oracle))));
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == (Token::UPDATE.name->c_str()))
    {
      SQLStatement_ptr stmt = parseUpdateStatement();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == (Token::CREATE.name->c_str()))
    {
      SQLStatement_ptr stmt = parseCreate();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::INSERT.name->c_str())
    {
      SQLStatement_ptr stmt = parseInsert();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == (Token::DELETE.name->c_str()))
    {
      SQLStatement_ptr stmt = parseDeleteStatement();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == (Token::SLASH.name->c_str()))
    {
      lexer->nextToken();

      SQLStatement_ptr stmt = SQLScriptCommitStatement_ptr(new SQLScriptCommitStatement());
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::ALTER.name->c_str())
    {
      SQLStatement_ptr stmt = parserAlter();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::WITH.name->c_str())
    {
      SQLSelectStatement_ptr stmt = SQLSelectStatement_ptr(new SQLSelectStatement(this->createSQLSelectParser()->select(), dbType));
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::LBRACE.name->c_str() ||
        lexer->identifierEquals(make_string_ptr("CALL")))
    {
      SQLStatement_ptr stmt = parseCall();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::MERGE.name->c_str())
    {
      SQLStatement_ptr stmt = parseMerge();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::BEGIN.name->c_str() || lexer->token()->name->c_str() == Token::DECLARE.name->c_str())
    {
      SQLStatement_ptr stmt = parseBlock();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::LOCK.name->c_str())
    {
      SQLStatement_ptr stmt = parseLock();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::TRUNCATE.name->c_str())
    {
      SQLStatement_ptr stmt = parseTruncate();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::VARIANT.name->c_str())
    {
      SQLExpr_ptr variant = this->exprParser->primary();
      if (instanceof <SQLExpr, SQLBinaryOpExpr>(variant))
      {
        SQLBinaryOpExpr_ptr binaryOpExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(variant);
        if (binaryOpExpr->getOperator()->name->c_str() == SQLBinaryOperator::Assignment.name->c_str())
        {
          SQLSetStatement_ptr stmt = SQLSetStatement_ptr(new SQLSetStatement(binaryOpExpr->getLeft(), binaryOpExpr->getRight(), getDbType()));
          stmt->setParent(parent);
          statementList->push_back(stmt);
          continue;
        }
      }
      accept(Token::COLONEQ);
      SQLExpr_ptr value = this->exprParser->expr();

      SQLSetStatement_ptr stmt = SQLSetStatement_ptr(new SQLSetStatement(variant, value, getDbType()));
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::EXCEPTION.name->c_str())
    {
      OracleExceptionStatement_ptr stmt = this->parseException();
      stmt->setParent(parent);
      if (instanceof <SQLObject, SQLBlockStatement>(parent))
      {
        (std::dynamic_pointer_cast<SQLBlockStatement>(parent))->setException(stmt);
      }
      else
      {
        statementList->push_back(stmt);
      }
      continue;
    }

    if (lexer->identifierEquals(make_string_ptr("EXIT")))
    {
      lexer->nextToken();
      OracleExitStatement_ptr stmt = parseExit();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->identifierEquals(make_string_ptr("REFRESH")))
    {
      SQLStatement_ptr stmt = parseRefresh();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::CONTINUE.name->c_str())
    {
      lexer->nextToken();
      OracleContinueStatement_ptr stmt = OracleContinueStatement_ptr(new OracleContinueStatement());

      if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
      {
        string_ptr label = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        lexer->nextToken();
        stmt->setLabel(label);
      }
      if (lexer->token()->name->c_str() == Token::WHEN.name->c_str())
      {
        lexer->nextToken();
        stmt->setWhen(this->exprParser->expr());
      }
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::FETCH.name->c_str() ||
        lexer->identifierEquals(make_string_ptr("FETCH")))
    {
      SQLStatement_ptr stmt = parseFetch();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->identifierEquals(make_string_ptr("ROLLBACK")))
    {
      SQLStatement_ptr stmt = parseRollback();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::EXPLAIN.name->c_str())
    {
      OracleExplainStatement_ptr stmt = std::dynamic_pointer_cast<OracleExplainStatement>(this->parseExplain());
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
    {
      string_ptr strVal = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      if (StringUtils::equalsIgnoreCase(strVal, make_string_ptr("RAISE")))
      {
        SQLStatement_ptr stmt = this->parseRaise();
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (StringUtils::equalsIgnoreCase(strVal, make_string_ptr("FORALL")))
      {
        SQLStatement_ptr stmt = this->parseFor();
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (StringUtils::equalsIgnoreCase(strVal, make_string_ptr("RENAME")))
      {
        SQLStatement_ptr stmt = this->parseRename();
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (StringUtils::equalsIgnoreCase(strVal, make_string_ptr("EXECUTE")))
      {
        SQLStatement_ptr stmt = this->parseExecute();
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (StringUtils::equalsIgnoreCase(strVal, make_string_ptr("ANALYZE")))
      {
        acceptIdentifier("ANALYZE");
        if (lexer->token()->name->c_str() == Token::TABLE.name->c_str())
        {
          SQLAnalyzeTableStatement_ptr stmt = this->parseAnalyzeTable();
          stmt->setParent(parent);
          statementList->push_back(stmt);
          continue;
        }
      }

      if (StringUtils::equalsIgnoreCase(strVal, make_string_ptr("PIPE")))
      {
        Lexer_SavePoint_ptr savePoint = lexer->mark();
        lexer->nextToken();

        if (lexer->token()->name->c_str() == Token::ROW.name->c_str())
        {
          lexer->reset(savePoint);
          SQLStatement_ptr stmt = this->parsePipeRow();
          stmt->setParent(parent);
          statementList->push_back(stmt);
        }
        else
        {
          lexer->reset(savePoint);
        }
        continue;
      }

      if (StringUtils::equalsIgnoreCase(strVal, make_string_ptr("SHOW")))
      {
        //                    Lexer.SavePoint savePoint = lexer->mark();
        lexer->nextToken();

        if (lexer->identifierEquals(make_string_ptr("ERR")))
        {
          lexer->nextToken();
        }
        else
        {
          accept(Token::ERRORS);
        }

        SQLShowErrorsStatement_ptr stmt = SQLShowErrorsStatement_ptr(new SQLShowErrorsStatement());
        stmt->setDbType(dbType);
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      SQLExpr_ptr expr = exprParser->expr();

      if (instanceof <SQLExpr, SQLBinaryOpExpr>(expr))
      {
        SQLBinaryOpExpr_ptr binaryOpExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(expr);
        if (binaryOpExpr->getOperator()->name->c_str() == SQLBinaryOperator::Assignment.name->c_str())
        {
          SQLSetStatement_ptr stmt = SQLSetStatement_ptr(new SQLSetStatement());
          stmt->setDbType(DbType_ptr(new DbType(&DbType::oracle)));
          stmt->setParent(parent);

          SQLAssignItem_ptr assignItem = SQLAssignItem_ptr(new SQLAssignItem(binaryOpExpr->getLeft(), binaryOpExpr->getRight()));
          assignItem->setParent(stmt);
          stmt->getItems()->push_back(assignItem);

          statementList->push_back(stmt);

          continue;
        }
      }

      SQLExprStatement_ptr stmt = SQLExprStatement_ptr(new SQLExprStatement(expr));
      stmt->setDbType(dbType);
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      Lexer_SavePoint_ptr savePoint = lexer->mark();
      lexer->nextToken();

      int parenCount = 0;
      while (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        savePoint = lexer->mark();
        lexer->nextToken();
        parenCount++;
      }

      if (lexer->token()->name->c_str() == Token::SELECT.name->c_str())
      {
        lexer->reset(savePoint);

        SQLStatement_ptr stmt = parseSelect();
        stmt->setParent(parent);
        statementList->push_back(stmt);

        for (int i = 0; i < parenCount; ++i)
        {
          accept(Token::RPAREN);
        }
        continue;
      }
      else
      {
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
    }

    if (lexer->token()->name->c_str() == Token::SET.name->c_str())
    {
      SQLStatement_ptr stmt = parseSet();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::GRANT.name->c_str())
    {
      statementList->push_back(this->parseGrant());
      continue;
    }

    if (lexer->token()->name->c_str() == Token::REVOKE.name->c_str())
    {
      statementList->push_back(this->parseRevoke());
      continue;
    }

    if (lexer->token()->name->c_str() == Token::COMMENT.name->c_str())
    {
      statementList->push_back(this->parseComment());
      continue;
    }
    if (lexer->token()->name->c_str() == Token::FOR.name->c_str())
    {
      OracleForStatement_ptr forStatement = this->parseFor();
      forStatement->setParent(parent);
      if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
      {
        string_ptr strVal = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        int stmtListSize = statementList->size();
        if (stmtListSize > 0)
        {
          auto it_statementList = statementList->begin();
          std::advance(it_statementList, stmtListSize - 1);
          SQLStatement_ptr lastStmt = *it_statementList;
          if (instanceof <SQLStatement, OracleLabelStatement>(lastStmt))
          {
            if (StringUtils::equalsIgnoreCase((std::dynamic_pointer_cast<OracleLabelStatement>(lastStmt))->getLabel()->getSimpleName(), strVal))
            {
              SQLName_ptr endLabbel = this->exprParser->name();
              forStatement->setEndLabel(endLabbel);
            }
          }
        }
      }
      statementList->push_back(forStatement);
      continue;
    }
    if (lexer->token()->name->c_str() == Token::LOOP.name->c_str())
    {
      SQLStatement_ptr stmt = std::dynamic_pointer_cast<SQLStatement>(parseLoop());
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }
    if (lexer->token()->name->c_str() == Token::IF.name->c_str())
    {
      SQLStatement_ptr stmt = parseIf();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::GOTO.name->c_str())
    {
      lexer->nextToken();
      SQLName_ptr label = this->exprParser->name();
      OracleGotoStatement_ptr stmt = OracleGotoStatement_ptr(new OracleGotoStatement(label));
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::COMMIT.name->c_str())
    {
      lexer->nextToken();

      if (lexer->identifierEquals(make_string_ptr("WORK")))
      {
        lexer->nextToken();
      }
      SQLCommitStatement_ptr stmt = SQLCommitStatement_ptr(new SQLCommitStatement());
      stmt->setParent(parent);

      if (lexer->identifierEquals(make_string_ptr("WRITE")))
      {
        stmt->setWrite(BOOL::TRUE);
        lexer->nextToken();

        for (;;)
        {
          if (lexer->token()->name->c_str() == Token::WAIT.name->c_str())
          {
            lexer->nextToken();
            stmt->setWait(BOOL::TRUE);
            continue;
          }
          else if (lexer->token()->name->c_str() == Token::NOWAIT.name->c_str())
          {
            lexer->nextToken();
            stmt->setWait(BOOL::FALSE);
            continue;
          }
          else if (lexer->token()->name->c_str() == Token::IMMEDIATE.name->c_str())
          {
            lexer->nextToken();
            stmt->setImmediate(BOOL::TRUE);
            continue;
          }
          else if (lexer->identifierEquals(make_string_ptr("BATCH")))
          {
            lexer->nextToken();
            stmt->setImmediate(BOOL::FALSE);
            continue;
          }

          break;
        }
      }

      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::SAVEPOINT.name->c_str())
    {
      lexer->nextToken();

      SQLSavePointStatement_ptr stmt = SQLSavePointStatement_ptr(new SQLSavePointStatement());
      stmt->setDbType(dbType);
      stmt->setParent(parent);

      if (lexer->token()->name->c_str() == Token::TO.name->c_str())
      {
        lexer->nextToken();
        stmt->setName(this->exprParser->name());
      }
      else if (lexer->token()->name->c_str() != Token::SEMI.name->c_str())
      {
        stmt->setName(this->exprParser->name());
      }
      accept(Token::SEMI);
      stmt->setAfterSemi(BOOL::TRUE);

      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::LTLT.name->c_str())
    {
      lexer->nextToken();
      SQLName_ptr label = this->exprParser->name();
      OracleLabelStatement_ptr stmt = OracleLabelStatement_ptr(new OracleLabelStatement(label));
      accept(Token::GTGT);
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::DROP.name->c_str())
    {
      Lexer_SavePoint_ptr savePoint = lexer->mark();
      lexer->nextToken();

      if (lexer->token()->name->c_str() == Token::TABLE.name->c_str())
      {
        SQLDropTableStatement_ptr stmt = parseDropTable(BOOL::FALSE);
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      BOOL_ptr isPublic = BOOL::FALSE;
      if (lexer->identifierEquals(make_string_ptr("PUBLIC")))
      {
        lexer->nextToken();
        isPublic = BOOL::TRUE;
      }

      if (lexer->token()->name->c_str() == Token::DATABASE.name->c_str())
      {
        lexer->nextToken();

        if (lexer->identifierEquals(make_string_ptr("LINK")))
        {
          lexer->nextToken();

          OracleDropDbLinkStatement_ptr stmt = OracleDropDbLinkStatement_ptr(new OracleDropDbLinkStatement());
          if (isPublic)
          {
            stmt->setPublic(isPublic);
          }

          stmt->setName(this->exprParser->name());

          statementList->push_back(stmt);
          continue;
        }
      }

      if (lexer->token()->name->c_str() == Token::INDEX.name->c_str())
      {
        SQLStatement_ptr stmt = parseDropIndex();
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (lexer->token()->name->c_str() == Token::VIEW.name->c_str())
      {
        SQLStatement_ptr stmt = parseDropView(BOOL::FALSE);
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (lexer->token()->name->c_str() == Token::SEQUENCE.name->c_str())
      {
        SQLDropSequenceStatement_ptr stmt = parseDropSequence(BOOL::FALSE);
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (lexer->token()->name->c_str() == Token::TRIGGER.name->c_str())
      {
        SQLDropTriggerStatement_ptr stmt = parseDropTrigger(BOOL::FALSE);
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (lexer->token()->name->c_str() == Token::USER.name->c_str())
      {
        SQLDropUserStatement_ptr stmt = parseDropUser();
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (lexer->token()->name->c_str() == Token::PROCEDURE.name->c_str())
      {
        SQLDropProcedureStatement_ptr stmt = parseDropProcedure(BOOL::FALSE);
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (lexer->identifierEquals(FnvHash::Constants::SYNONYM))
      {
        lexer->reset(savePoint);

        SQLStatement_ptr stmt = parseDropSynonym();
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (lexer->identifierEquals(FnvHash::Constants::TYPE))
      {
        lexer->reset(savePoint);

        SQLStatement_ptr stmt = parseDropType();
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      if (lexer->identifierEquals(FnvHash::Constants::MATERIALIZED))
      {
        lexer->reset(savePoint);

        SQLStatement_ptr stmt = parseDropMaterializedView();
        stmt->setParent(parent);
        statementList->push_back(stmt);
        continue;
      }

      std::string tmp = "TODO : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }

    if (lexer->token()->name->c_str() == Token::XNULL.name->c_str())
    {
      lexer->nextToken();
      SQLExprStatement_ptr stmt = SQLExprStatement_ptr(new SQLExprStatement(SQLNullExpr_ptr(new SQLNullExpr())));
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::OPEN.name->c_str())
    {
      SQLStatement_ptr stmt = this->parseOpen();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::CLOSE.name->c_str())
    {
      SQLStatement_ptr stmt = this->parseClose();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::CASE.name->c_str())
    {
      SQLStatement_ptr stmt = this->parseCase();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::PROCEDURE.name->c_str())
    {
      SQLStatement_ptr stmt = this->parseCreateProcedure();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::ELSIF.name->c_str() && instanceof <SQLObject, SQLIfStatement>(parent))
    {
      break;
    }

    if (lexer->token()->name->c_str() == Token::WHEN.name->c_str() && instanceof <SQLObject, OracleExceptionStatement_Item>(parent))
    {
      break;
    }

    if (lexer->token()->name->c_str() == Token::FUNCTION.name->c_str())
    {
      SQLStatement_ptr stmt = this->parseFunction();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::WHILE.name->c_str())
    {
      SQLStatement_ptr stmt = this->parseWhile();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::RETURN.name->c_str())
    {
      SQLStatement_ptr stmt = this->parseReturn();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::TRIGGER.name->c_str())
    {
      SQLStatement_ptr stmt = this->parseCreateTrigger();
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::MONKEYS_AT_AT.name->c_str())
    {
      lexer->nextToken();

      SQLExpr_ptr expr = exprParser->primary();

      OracleRunStatement_ptr stmt = OracleRunStatement_ptr(new OracleRunStatement(expr));
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::QUES.name->c_str())
    {
      SQLExpr_ptr expr = this->exprParser->expr();
      SQLExprStatement_ptr stmt = SQLExprStatement_ptr(new SQLExprStatement(expr));
      stmt->setParent(parent);
      statementList->push_back(stmt);
      continue;
    }

    std::string tmp = "TODO : ";
    tmp += encode_util::UnicodeToUTF8(lexer->info());
    throw new ParserException(make_string_ptr(tmp));
  }
}

SQLStatement_ptr OracleStatementParser::parseDropType()
{
  if (lexer->token()->name->c_str() == Token::DROP.name->c_str())
  {
    lexer->nextToken();
  }
  SQLDropTypeStatement_ptr stmt = SQLDropTypeStatement_ptr(new SQLDropTypeStatement());
  stmt->setDbType(dbType);

  acceptIdentifier("TYPE");

  stmt->setName(this->exprParser->name());
  return stmt;
}

// @Override
SQLStatement_ptr OracleStatementParser::parseDropMaterializedView()
{
  if (lexer->token()->name->c_str() == Token::DROP.name->c_str())
  {
    lexer->nextToken();
  }
  SQLDropMaterializedViewStatement_ptr stmt = SQLDropMaterializedViewStatement_ptr(new SQLDropMaterializedViewStatement());
  stmt->setDbType(dbType);

  acceptIdentifier("MATERIALIZED");

  accept(Token::VIEW);

  stmt->setName(this->exprParser->name());
  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseDropSynonym()
{
  if (lexer->token()->name->c_str() == Token::DROP.name->c_str())
  {
    lexer->nextToken();
  }
  SQLDropSynonymStatement_ptr stmt = SQLDropSynonymStatement_ptr(new SQLDropSynonymStatement());
  stmt->setDbType(dbType);

  if (lexer->identifierEquals(FnvHash::Constants::PUBLIC))
  {
    lexer->nextToken();
    stmt->setPublic(BOOL::TRUE);
  }

  acceptIdentifier("SYNONYM");

  stmt->setName(this->exprParser->name());

  if (lexer->identifierEquals(FnvHash::Constants::FORCE))
  {
    lexer->nextToken();
    stmt->setForce(BOOL::TRUE);
  }

  return stmt;
}

SQLStatement_ptr OracleStatementParser::parsePipeRow()
{
  OraclePipeRowStatement_ptr stmt = OraclePipeRowStatement_ptr(new OraclePipeRowStatement());
  acceptIdentifier("PIPE");
  accept(Token::ROW);
  accept(Token::LPAREN);
  this->exprParser->exprList(stmt->getParameters(), stmt);
  accept(Token::RPAREN);
  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseExecute()
{
  acceptIdentifier("EXECUTE");

  if (lexer->token()->name->c_str() == Token::IMMEDIATE.name->c_str())
  {
    lexer->nextToken();

    OracleExecuteImmediateStatement_ptr stmt = OracleExecuteImmediateStatement_ptr(new OracleExecuteImmediateStatement());

    SQLExpr_ptr dyanmiacSql = this->exprParser->primary();
    stmt->setDynamicSql(dyanmiacSql);

    if (lexer->token()->name->c_str() == Token::INTO.name->c_str())
    {
      lexer->nextToken();

      this->exprParser->exprList(stmt->getInto(), stmt);
    }

    if (lexer->token()->name->c_str() == Token::USING.name->c_str())
    {
      lexer->nextToken();

      for (;;)
      {
        SQLArgument_ptr arg = SQLArgument_ptr(new SQLArgument());

        if (lexer->token()->name->c_str() == Token::IN.name->c_str())
        {
          lexer->nextToken();
          if (lexer->token()->name->c_str() == Token::OUT.name->c_str())
          {
            lexer->nextToken();
            arg->setType(SQLParameter_ParameterType_ptr(new SQLParameter_ParameterType(&SQLParameter_ParameterType::INOUT)));
          }
          else
          {
            arg->setType(SQLParameter_ParameterType_ptr(new SQLParameter_ParameterType(&SQLParameter_ParameterType::IN)));
          }
        }
        else if (lexer->token()->name->c_str() == Token::OUT.name->c_str())
        {
          lexer->nextToken();
          arg->setType(SQLParameter_ParameterType_ptr(new SQLParameter_ParameterType(&SQLParameter_ParameterType::OUT)));
        }

        arg->setExpr(this->exprParser->primary());
        arg->setParent(stmt);
        stmt->getArguments()->push_back(arg);

        if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
        {
          lexer->nextToken();
          continue;
        }
        break;
      }
    }

    if (lexer->token()->name->c_str() == Token::RETURNING.name->c_str())
    {
      lexer->nextToken();
      accept(Token::INTO);

      this->exprParser->exprList(stmt->getReturnInto(), stmt);
    }

    return stmt;
  }
  std::string tmp = "TODO : ";
  tmp += encode_util::UnicodeToUTF8(lexer->info());
  throw new ParserException(make_string_ptr(tmp));
}

SQLStatement_ptr OracleStatementParser::parseRename()
{
  lexer->nextToken();
  SQLName_ptr from = this->exprParser->name();
  accept(Token::TO);
  SQLName_ptr to = this->exprParser->name();

  SQLAlterTableStatement_ptr stmt = SQLAlterTableStatement_ptr(new SQLAlterTableStatement(dbType));
  stmt->setTableSource(from);
  SQLAlterTableRename_ptr toItem = SQLAlterTableRename_ptr(new SQLAlterTableRename(to));
  stmt->addItem(toItem);

  return stmt;
}

OracleExitStatement_ptr OracleStatementParser::parseExit()
{
  OracleExitStatement_ptr stmt = OracleExitStatement_ptr(new OracleExitStatement());

  if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
  {
    string_ptr label = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    stmt->setLabel(label);
    lexer->nextToken();
  }

  if (lexer->token()->name->c_str() == Token::WHEN.name->c_str())
  {
    lexer->nextToken();
    stmt->setWhen(this->exprParser->expr());
  }
  accept(Token::SEMI);
  stmt->setAfterSemi(BOOL::TRUE);
  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseReturn()
{
  accept(Token::RETURN);
  SQLReturnStatement_ptr stmt = SQLReturnStatement_ptr(new SQLReturnStatement());
  if (lexer->token()->name->c_str() != Token::SEMI.name->c_str())
  {
    SQLExpr_ptr expr = this->exprParser->expr();
    stmt->setExpr(expr);
  }

  accept(Token::SEMI);
  stmt->setAfterSemi(BOOL::TRUE);

  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseWhile()
{
  accept(Token::WHILE);

  SQLWhileStatement_ptr stmt = SQLWhileStatement_ptr(new SQLWhileStatement());
  stmt->setDbType(dbType);

  stmt->setCondition(this->exprParser->expr());

  accept(Token::LOOP);

  this->parseStatementList(stmt->getStatements(), -1, stmt);
  accept(Token::END);
  accept(Token::LOOP);
  accept(Token::SEMI);

  return stmt;
}

SQLCreateFunctionStatement_ptr OracleStatementParser::parseCreateFunction()
{
  SQLCreateFunctionStatement_ptr stmt = std::dynamic_pointer_cast<SQLCreateFunctionStatement>(parseFunction());
  stmt->setCreate(BOOL::TRUE);
  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseFunction()
{
  SQLCreateFunctionStatement_ptr stmt = SQLCreateFunctionStatement_ptr(new SQLCreateFunctionStatement());
  stmt->setDbType(dbType);

  if (lexer->token()->name->c_str() == Token::CREATE.name->c_str())
  {
    lexer->nextToken();
    if (lexer->token()->name->c_str() == Token::OR.name->c_str())
    {
      lexer->nextToken();
      accept(Token::REPLACE);
      stmt->setOrReplace(BOOL::TRUE);
    }
  }
  else
  {
    if (lexer->token()->name->c_str() == Token::DECLARE.name->c_str())
    {
      lexer->nextToken();
    }
    stmt->setCreate(BOOL::FALSE);
  }

  accept(Token::FUNCTION);

  SQLName_ptr functionName = this->exprParser->name();
  stmt->setName(functionName);

  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();
    parserParameters(stmt->getParameters(), stmt);
    accept(Token::RPAREN);
  }

  if (lexer->identifierEquals(FnvHash::Constants::WRAPPED))
  {
    lexer->nextToken();
    int pos = lexer->text.find(L';', lexer->pos());
    if (pos != std::wstring::npos)
    {
      string_ptr wrappedString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->subString(lexer->pos(), pos - lexer->pos())));
      stmt->setWrappedSource(wrappedString);
      lexer->reset(pos, ';', Token_ptr(new Token(&Token::LITERAL_CHARS)));
      lexer->nextToken();
      stmt->setAfterSemi(BOOL::TRUE);
    }
    else
    {
      string_ptr wrappedString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->subString(lexer->pos())));
      stmt->setWrappedSource(wrappedString);
      lexer->reset(lexer->text.length(), (char)LayoutCharacters::EOI, Token_ptr(new Token(&Token::XEOF)));
      return stmt;
    }

    return stmt;
  }

  accept(Token::RETURN);
  SQLDataType_ptr returnDataType = this->exprParser->parseDataType(BOOL::FALSE);
  stmt->setReturnDataType(returnDataType);

  if (identifierEquals("PIPELINED"))
  {
    lexer->nextToken();
    stmt->setPipelined(BOOL::TRUE);
  }

  if (identifierEquals("DETERMINISTIC"))
  {
    lexer->nextToken();
    stmt->setDeterministic(BOOL::TRUE);
  }

  if (lexer->identifierEquals(FnvHash::Constants::AUTHID))
  {
    lexer->nextToken();
    string_ptr strVal = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    if (lexer->identifierEquals(FnvHash::Constants::CURRENT_USER))
    {
      lexer->nextToken();
    }
    else
    {
      acceptIdentifier("DEFINER");
    }
    SQLName_ptr authid = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(strVal));
    stmt->setAuthid(authid);
  }

  if (identifierEquals("RESULT_CACHE"))
  {
    lexer->nextToken();
    stmt->setResultCache(BOOL::TRUE);
  }

  if (lexer->token()->name->c_str() == Token::SEMI.name->c_str())
  {
    lexer->nextToken();
    return stmt;
  }

  if (lexer->token()->name->c_str() == Token::IS.name->c_str() || lexer->token()->name->c_str() == Token::AS.name->c_str())
  {
    lexer->nextToken();
  }

  if (lexer->identifierEquals(make_string_ptr("LANGUAGE")))
  {
    lexer->nextToken();
    if (lexer->identifierEquals(make_string_ptr("JAVA")))
    {
      lexer->nextToken();
      acceptIdentifier("NAME");
      string_ptr javaCallSpec = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      accept(Token::LITERAL_CHARS);
      stmt->setJavaCallSpec(javaCallSpec);
    }
    else
    {
      std::string tmp = "TODO : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
    return stmt;
  }

  if (lexer->identifierEquals(make_string_ptr("PARALLEL_ENABLE")))
  {
    lexer->nextToken();
    stmt->setParallelEnable(BOOL::TRUE);
  }

  if (lexer->identifierEquals(make_string_ptr("AGGREGATE")))
  {
    lexer->nextToken();
    stmt->setAggregate(BOOL::TRUE);
  }

  if (lexer->token()->name->c_str() == Token::USING.name->c_str())
  {
    lexer->nextToken();
    SQLName_ptr using_ = this->exprParser->name();
    stmt->setUsing(using_);
  }

  SQLStatement_ptr block;
  if (lexer->token()->name->c_str() == Token::SEMI.name->c_str())
  {
    stmt->setAfterSemi(BOOL::TRUE);
    lexer->nextToken();
    block = nullptr;
  }
  else
  {
    block = this->parseBlock();
  }

  stmt->setBlock(block);

  if (lexer->identifierEquals(functionName->getSimpleName()))
  {
    lexer->nextToken();
  }

  // return stmt;

  if (lexer->identifierEquals(functionName->getSimpleName()))
  {
    lexer->nextToken();
  }

  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseRaise()
{
  lexer->nextToken();
  OracleRaiseStatement_ptr stmt = OracleRaiseStatement_ptr(new OracleRaiseStatement());
  if (lexer->token()->name->c_str() != Token::SEMI.name->c_str())
  {
    stmt->setException(this->exprParser->expr());
  }
  accept(Token::SEMI);
  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseCase()
{
  SQLCaseStatement_ptr caseStmt = SQLCaseStatement_ptr(new SQLCaseStatement());
  caseStmt->setDbType(dbType);
  lexer->nextToken();
  if (lexer->token()->name->c_str() != Token::WHEN.name->c_str())
  {
    caseStmt->setValueExpr(this->exprParser->expr());
  }

  accept(Token::WHEN);
  SQLExpr_ptr testExpr = this->exprParser->expr();
  accept(Token::THEN);
  SQLStatement_ptr stmt = this->parseStatement();
  if (lexer->token()->name->c_str() == Token::SEMI.name->c_str())
  {
    lexer->nextToken();
  }
  SQLCaseStatement_Item_ptr caseItem = SQLCaseStatement_Item_ptr(new SQLCaseStatement_Item(testExpr, stmt));
  caseStmt->addItem(caseItem);

  while (lexer->token()->name->c_str() == Token::WHEN.name->c_str())
  {
    lexer->nextToken();
    testExpr = this->exprParser->expr();
    accept(Token::THEN);
    stmt = this->parseStatement();
    if (lexer->token()->name->c_str() == Token::SEMI.name->c_str())
    {
      lexer->nextToken();
    }
    caseItem = SQLCaseStatement_Item_ptr(new SQLCaseStatement_Item(testExpr, stmt));
    caseStmt->addItem(caseItem);
  }

  if (lexer->token()->name->c_str() == Token::ELSE.name->c_str())
  {
    lexer->nextToken();
    this->parseStatementList(caseStmt->getElseStatements(), -1, caseStmt);
  }

  accept(Token::END);
  accept(Token::CASE);
  accept(Token::SEMI);
  return caseStmt;
}

SQLStatement_ptr OracleStatementParser::parseIf()
{
  accept(Token::IF);

  SQLIfStatement_ptr stmt = SQLIfStatement_ptr(new SQLIfStatement());
  stmt->setDbType(dbType);

  stmt->setCondition(this->exprParser->expr());

  accept(Token::THEN);

  this->parseStatementList(stmt->getStatements(), -1, stmt);

  while (lexer->token()->name->c_str() == Token::ELSIF.name->c_str())
  {
    lexer->nextToken();

    SQLIfStatement_ElseIf_ptr elseIf = SQLIfStatement_ElseIf_ptr(new SQLIfStatement_ElseIf());

    elseIf->setCondition(this->exprParser->expr());
    elseIf->setParent(stmt);

    accept(Token::THEN);
    this->parseStatementList(elseIf->getStatements(), -1, stmt);

    stmt->getElseIfList()->push_back(elseIf);
  }

  if (lexer->token()->name->c_str() == Token::ELSE.name->c_str())
  {
    lexer->nextToken();

    SQLIfStatement_Else_ptr elseItem = SQLIfStatement_Else_ptr(new SQLIfStatement_Else());
    this->parseStatementList(elseItem->getStatements(), -1, elseItem);
    stmt->setElseItem(elseItem);
  }

  accept(Token::END);
  //        if (lexer->token()->name->c_str() != Token::SEMI) {
  //            accept(Token::IF);
  //        }
  accept(Token::IF);
  //        if (lexer->token()->name->c_str() == Token::SEMI) {
  //            lexer->nextToken();
  //        }
  accept(Token::SEMI);
  stmt->setAfterSemi(BOOL::TRUE);

  return stmt;
}

// @Override
OracleForStatement_ptr OracleStatementParser::parseFor()
{
  OracleForStatement_ptr stmt = OracleForStatement_ptr(new OracleForStatement());

  if (lexer->token()->name->c_str() == Token::FOR.name->c_str())
  {
    lexer->nextToken();
  }
  else
  {
    acceptIdentifier("FORALL");
    stmt->setAll(BOOL::TRUE);
  }

  stmt->setIndex(this->exprParser->name());
  accept(Token::IN);
  stmt->setRange(this->exprParser->expr());

  if (stmt->isAll())
  {
    SQLStatement_ptr itemStmt = this->parseStatement();
    itemStmt->setParent(stmt);
    stmt->getStatements()->push_back(itemStmt);
  }
  else
  {
    accept(Token::LOOP);

    this->parseStatementList(stmt->getStatements(), -1, stmt);
    accept(Token::END);
    accept(Token::LOOP);

    if (lexer->token()->name->c_str() != Token::SEMI.name->c_str())
    {
      SQLName_ptr endLabel = this->exprParser->name();
      stmt->setEndLabel(endLabel);
    }

    accept(Token::SEMI);
    stmt->setAfterSemi(BOOL::TRUE);
  }
  return stmt;
}

SQLLoopStatement_ptr OracleStatementParser::parseLoop()
{
  accept(Token::LOOP);

  SQLLoopStatement_ptr stmt = SQLLoopStatement_ptr(new SQLLoopStatement());

  this->parseStatementList(stmt->getStatements(), -1, stmt);
  accept(Token::END);
  accept(Token::LOOP);

  if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
  {
    string_ptr label = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    stmt->setLabelName(label);
    lexer->nextToken();
  }

  accept(Token::SEMI);
  stmt->setAfterSemi(BOOL::TRUE);
  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseSet()
{
  accept(Token::SET);

  if (lexer->identifierEquals(make_string_ptr("TRANSACTION")))
  {
    lexer->nextToken();

    OracleSetTransactionStatement_ptr stmt = OracleSetTransactionStatement_ptr(new OracleSetTransactionStatement());

    if (lexer->identifierEquals(make_string_ptr("READ")))
    {
      lexer->nextToken();

      if (lexer->identifierEquals(make_string_ptr("ONLY")))
      {
        lexer->nextToken();
        stmt->setReadOnly(BOOL::TRUE);
      }
      else
      {
        acceptIdentifier("WRITE");
        stmt->setWrite(BOOL::TRUE);
      }
    }

    if (lexer->identifierEquals(make_string_ptr("NAME")))
    {
      lexer->nextToken();

      stmt->setName(this->exprParser->expr());
    }

    return stmt;
  }

  SQLSetStatement_ptr stmt = SQLSetStatement_ptr(new SQLSetStatement(dbType));
  parseAssignItems(stmt->getItems(), stmt);

  Object_ptr value = Object_ptr(new Object());
  value->any_data_ = BOOL::TRUE;
  stmt->putAttribute("parser.set", value);
  return stmt;
}

SQLStatement_ptr OracleStatementParser::parserAlter()
{
  Lexer_SavePoint_ptr savePoint = lexer->mark();
  accept(Token::ALTER);
  if (lexer->token()->name->c_str() == Token::SESSION.name->c_str())
  {
    lexer->nextToken();

    OracleAlterSessionStatement_ptr stmt = OracleAlterSessionStatement_ptr(new OracleAlterSessionStatement());
    if (lexer->token()->name->c_str() == Token::SET.name->c_str())
    {
      lexer->nextToken();
      parseAssignItems(stmt->getItems(), stmt);
    }
    else
    {
      std::string tmp = "TODO : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
    return stmt;
  }
  else if (lexer->token()->name->c_str() == Token::PROCEDURE.name->c_str())
  {
    lexer->nextToken();
    SQLAlterProcedureStatement_ptr stmt = SQLAlterProcedureStatement_ptr(new SQLAlterProcedureStatement());
    stmt->setName(this->exprParser->name());
    if (lexer->identifierEquals(make_string_ptr("COMPILE")))
    {
      lexer->nextToken();
      stmt->setCompile(BOOL::TRUE);
    }

    if (lexer->identifierEquals(make_string_ptr("REUSE")))
    {
      lexer->nextToken();
      acceptIdentifier("SETTINGS");
      stmt->setReuseSettings(BOOL::TRUE);
    }

    return stmt;
  }
  else if (lexer->token()->name->c_str() == Token::TABLE.name->c_str())
  {
    return parseAlterTable();
  }
  else if (lexer->token()->name->c_str() == Token::INDEX.name->c_str())
  {
    lexer->reset(savePoint);
    return parseAlterIndex();
  }
  else if (lexer->token()->name->c_str() == Token::TRIGGER.name->c_str())
  {
    lexer->nextToken();
    OracleAlterTriggerStatement_ptr stmt = OracleAlterTriggerStatement_ptr(new OracleAlterTriggerStatement());
    stmt->setName(this->exprParser->name());

    for (;;)
    {
      if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
      {
        lexer->nextToken();
        stmt->setEnable(BOOL::TRUE);
        continue;
      }
      else if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
      {
        lexer->nextToken();
        stmt->setEnable(BOOL::FALSE);
        continue;
      }
      else if (lexer->identifierEquals(make_string_ptr("COMPILE")))
      {
        lexer->nextToken();
        stmt->setCompile(BOOL::TRUE);
        continue;
      }
      break;
    }

    return stmt;
  }
  else if (lexer->identifierEquals(FnvHash::Constants::SYNONYM))
  {
    lexer->nextToken();
    OracleAlterSynonymStatement_ptr stmt = OracleAlterSynonymStatement_ptr(new OracleAlterSynonymStatement());
    stmt->setName(this->exprParser->name());

    for (;;)
    {
      if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
      {
        lexer->nextToken();
        stmt->setEnable(BOOL::TRUE);
        continue;
      }
      else if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
      {
        lexer->nextToken();
        stmt->setEnable(BOOL::FALSE);
        continue;
      }
      else if (lexer->identifierEquals(make_string_ptr("COMPILE")))
      {
        lexer->nextToken();
        stmt->setCompile(BOOL::TRUE);
        continue;
      }
      break;
    }

    return stmt;
  }
  else if (lexer->token()->name->c_str() == Token::VIEW.name->c_str())
  {
    lexer->nextToken();
    OracleAlterViewStatement_ptr stmt = OracleAlterViewStatement_ptr(new OracleAlterViewStatement());
    stmt->setName(this->exprParser->name());

    for (;;)
    {
      if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
      {
        lexer->nextToken();
        stmt->setEnable(BOOL::TRUE);
        continue;
      }
      else if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
      {
        lexer->nextToken();
        stmt->setEnable(BOOL::FALSE);
        continue;
      }
      else if (lexer->identifierEquals(make_string_ptr("COMPILE")))
      {
        lexer->nextToken();
        stmt->setCompile(BOOL::TRUE);
        continue;
      }
      break;
    }

    return stmt;
  }
  else if (lexer->token()->name->c_str() == Token::TABLESPACE.name->c_str())
  {
    lexer->nextToken();

    OracleAlterTablespaceStatement_ptr stmt = OracleAlterTablespaceStatement_ptr(new OracleAlterTablespaceStatement());
    stmt->setName(this->exprParser->name());

    if (lexer->identifierEquals(make_string_ptr("ADD")))
    {
      lexer->nextToken();

      if (lexer->identifierEquals(make_string_ptr("DATAFILE")))
      {
        lexer->nextToken();

        OracleAlterTablespaceAddDataFile_ptr item = OracleAlterTablespaceAddDataFile_ptr(new OracleAlterTablespaceAddDataFile());

        for (;;)
        {
          OracleFileSpecification_ptr file = OracleFileSpecification_ptr(new OracleFileSpecification());

          for (;;)
          {
            SQLExpr_ptr fileName = this->exprParser->expr();
            file->getFileNames()->push_back(fileName);

            if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
            {
              lexer->nextToken();
              continue;
            }

            break;
          }

          if (lexer->identifierEquals(make_string_ptr("SIZE")))
          {
            lexer->nextToken();
            file->setSize(this->exprParser->expr());
          }

          if (lexer->identifierEquals(make_string_ptr("AUTOEXTEND")))
          {
            lexer->nextToken();
            if (lexer->identifierEquals(make_string_ptr("OFF")))
            {
              lexer->nextToken();
              file->setAutoExtendOff(BOOL::TRUE);
            }
            else if (lexer->identifierEquals(make_string_ptr("ON")))
            {
              lexer->nextToken();
              file->setAutoExtendOn(this->exprParser->expr());
            }
            else
            {
              std::string tmp = "TODO : ";
              tmp += encode_util::UnicodeToUTF8(lexer->info());
              throw new ParserException(make_string_ptr(tmp));
            }
          }

          item->getFiles()->push_back(file);

          if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
          {
            lexer->nextToken();
            continue;
          }

          break;
        }

        stmt->setItem(item);
      }
      else
      {
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
    }
    else
    {
      std::string tmp = "TODO : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }

    return stmt;
  }
  else if (lexer->token()->name->c_str() == Token::FUNCTION.name->c_str())
  {
    lexer->reset(savePoint);
    return parseAlterFunction();
  }
  else if (lexer->token()->name->c_str() == Token::SEQUENCE.name->c_str())
  {
    lexer->reset(savePoint);
    return parseAlterSequence();
  }
  else if (lexer->identifierEquals(FnvHash::Constants::TYPE))
  {
    lexer->reset(savePoint);
    return parseAlterType();
  }
  else if (lexer->identifierEquals(make_string_ptr("SUMMARY")))
  {
    lexer->nextToken();

    OracleAlterSummaryStatement_ptr stmt = OracleAlterSummaryStatement_ptr(new OracleAlterSummaryStatement());
    stmt->setName(this->exprParser->name());

    if (lexer->identifierEquals(make_string_ptr("COMPILE")))
    {
      lexer->nextToken();
      stmt->setCompile(BOOL::TRUE);
    }

    return stmt;
  }

  std::string tmp = "TODO : ";
  tmp += encode_util::UnicodeToUTF8(lexer->info());
  throw new ParserException(make_string_ptr(tmp));
}

SQLStatement_ptr OracleStatementParser::parseAlterType()
{
  accept(Token::ALTER);
  acceptIdentifier("TYPE");

  SQLAlterTypeStatement_ptr stmt = SQLAlterTypeStatement_ptr(new SQLAlterTypeStatement());
  stmt->setDbType(dbType);

  SQLName_ptr name = this->exprParser->name();
  stmt->setName(name);

  if (lexer->identifierEquals(make_string_ptr("COMPILE")))
  {
    stmt->setCompile(BOOL::TRUE);
    lexer->nextToken();
  }

  if (lexer->identifierEquals(make_string_ptr("DEBUG")))
  {
    stmt->setDebug(BOOL::TRUE);
    lexer->nextToken();
  }

  if (lexer->identifierEquals(make_string_ptr("BODY")))
  {
    stmt->setBody(BOOL::TRUE);
    lexer->nextToken();
  }

  if (lexer->identifierEquals(make_string_ptr("REUSE")))
  {
    stmt->setReuseSettings(BOOL::TRUE);
    lexer->nextToken();
    acceptIdentifier("SETTINGS");
  }

  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseAlterFunction()
{
  accept(Token::ALTER);
  accept(Token::FUNCTION);

  SQLAlterFunctionStatement_ptr stmt = SQLAlterFunctionStatement_ptr(new SQLAlterFunctionStatement());
  stmt->setDbType(dbType);

  SQLName_ptr name = this->exprParser->name();
  stmt->setName(name);

  acceptIdentifier("COMPILE");

  if (lexer->identifierEquals(make_string_ptr("DEBUG")))
  {
    stmt->setDebug(BOOL::TRUE);
    lexer->nextToken();
  }

  if (lexer->identifierEquals(make_string_ptr("REUSE")))
  {
    stmt->setReuseSettings(BOOL::TRUE);
    lexer->nextToken();
    acceptIdentifier("SETTINGS");
  }
  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseAlterTable()
{
  lexer->nextToken();
  SQLAlterTableStatement_ptr stmt = SQLAlterTableStatement_ptr(new SQLAlterTableStatement(getDbType()));
  stmt->setName(this->exprParser->name());

  for (;;)
  {
    if (lexer->identifierEquals(FnvHash::Constants::ADD))
    {
      lexer->nextToken();

      if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        lexer->nextToken();

        SQLAlterTableAddColumn_ptr item = parseAlterTableAddColumn();

        stmt->addItem(item);

        accept(Token::RPAREN);
      }
      else if (lexer->token()->name->c_str() == Token::CONSTRAINT.name->c_str() || lexer->token()->name->c_str() == Token::FOREIGN.name->c_str() || lexer->token()->name->c_str() == Token::PRIMARY.name->c_str() || lexer->token()->name->c_str() == Token::UNIQUE.name->c_str() || lexer->token()->name->c_str() == Token::CHECK.name->c_str())
      {
        OracleConstraint_ptr constraint = (std::dynamic_pointer_cast<OracleExprParser>(this->exprParser))->parseConstaint();
        SQLAlterTableAddConstraint_ptr item = SQLAlterTableAddConstraint_ptr(new SQLAlterTableAddConstraint());
        constraint->setParent(item);
        item->setParent(stmt);
        item->setConstraint(constraint);
        stmt->addItem(item);
      }
      else if (lexer->identifierEquals(FnvHash::Constants::SUPPLEMENTAL))
      {
        SQLTableElement_ptr element = this->getSQLCreateTableParser()->parseCreateTableSupplementalLogingProps();

        SQLAlterTableAddSupplemental_ptr item = SQLAlterTableAddSupplemental_ptr(new SQLAlterTableAddSupplemental());
        item->setElement(element);
        stmt->addItem(item);
      }
      else if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
      {
        SQLAlterTableAddColumn_ptr item = parseAlterTableAddColumn();
        stmt->addItem(item);
      }
      else if (lexer->token()->name->c_str() == Token::LITERAL_ALIAS.name->c_str())
      {
        SQLAlterTableAddColumn_ptr item = parseAlterTableAddColumn();
        stmt->addItem(item);
      }
      else
      {
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }

      continue;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::MOVE))
    {
      lexer->nextToken();

      if (lexer->token()->name->c_str() == Token::TABLESPACE.name->c_str())
      {
        lexer->nextToken();

        OracleAlterTableMoveTablespace_ptr item = OracleAlterTableMoveTablespace_ptr(new OracleAlterTableMoveTablespace());
        item->setName(this->exprParser->name());

        stmt->addItem(item);
      }
      else
      {
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
    }
    else if (lexer->identifierEquals(make_string_ptr("RENAME")))
    {
      stmt->addItem(parseAlterTableRename());
    }
    else if (lexer->identifierEquals(make_string_ptr("MODIFY")))
    {
      lexer->nextToken();

      OracleAlterTableModify_ptr item = OracleAlterTableModify_ptr(new OracleAlterTableModify());
      if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        lexer->nextToken();

        for (;;)
        {
          SQLColumnDefinition_ptr columnDef = this->exprParser->parseColumn();
          item->addColumn(columnDef);
          if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
          {
            lexer->nextToken();
            continue;
          }
          break;
        }
        accept(Token::RPAREN);
      }
      else
      {
        SQLColumnDefinition_ptr columnDef = this->exprParser->parseColumn();
        item->addColumn(columnDef);
      }

      stmt->addItem(item);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("SPLIT")))
    {
      parseAlterTableSplit(stmt);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::TRUNCATE.name->c_str())
    {
      lexer->nextToken();
      if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
      {
        lexer->nextToken();
        OracleAlterTableTruncatePartition_ptr item = OracleAlterTableTruncatePartition_ptr(new OracleAlterTableTruncatePartition());
        item->setName(this->exprParser->name());
        stmt->addItem(item);
      }
      else
      {
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::DROP.name->c_str())
    {
      parseAlterDrop(stmt);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
    {
      lexer->nextToken();
      if (lexer->token()->name->c_str() == Token::CONSTRAINT.name->c_str())
      {
        lexer->nextToken();
        SQLAlterTableDisableConstraint_ptr item = SQLAlterTableDisableConstraint_ptr(new SQLAlterTableDisableConstraint());
        item->setConstraintName(this->exprParser->name());
        stmt->addItem(item);
      }
      else if (lexer->token()->name->c_str() == Token::ROW.name->c_str())
      {
        lexer->nextToken();
        OracleAlterTableRowMovement_ptr item = OracleAlterTableRowMovement_ptr(new OracleAlterTableRowMovement());
        acceptIdentifier("MOVEMENT");
        item->setEnable(BOOL::FALSE);
        stmt->addItem(item);
      }
      else
      {
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
    }
    else if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
    {
      lexer->nextToken();
      if (lexer->token()->name->c_str() == Token::CONSTRAINT.name->c_str())
      {
        lexer->nextToken();
        SQLAlterTableEnableConstraint_ptr item = SQLAlterTableEnableConstraint_ptr(new SQLAlterTableEnableConstraint());
        item->setConstraintName(this->exprParser->name());
        stmt->addItem(item);
      }
      else if (lexer->token()->name->c_str() == Token::ROW.name->c_str())
      {
        lexer->nextToken();
        OracleAlterTableRowMovement_ptr item = OracleAlterTableRowMovement_ptr(new OracleAlterTableRowMovement());
        acceptIdentifier("MOVEMENT");
        item->setEnable(BOOL::TRUE);
        stmt->addItem(item);
      }
      else
      {
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
    }
    else if (lexer->identifierEquals(make_string_ptr("SHRINK")))
    {
      lexer->nextToken();
      if (lexer->identifierEquals(make_string_ptr("SPACE")))
      {
        lexer->nextToken();

        OracleAlterTableShrinkSpace_ptr item = OracleAlterTableShrinkSpace_ptr(new OracleAlterTableShrinkSpace());
        fillShrinkSpace(item);
        fillShrinkSpace(item);
        fillShrinkSpace(item);

        stmt->addItem(item);
      }
    }

    break;
  }

  if (lexer->token()->name->c_str() == Token::UPDATE.name->c_str())
  {
    lexer->nextToken();

    if (lexer->identifierEquals(make_string_ptr("GLOBAL")))
    {
      lexer->nextToken();
      acceptIdentifier("INDEXES");
      stmt->setUpdateGlobalIndexes(BOOL::TRUE);
    }
    else
    {
      std::string tmp = "TODO : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
  }

  return stmt;
}

void OracleStatementParser::fillShrinkSpace(OracleAlterTableShrinkSpace_ptr item)
{
  if (lexer->identifierEquals(make_string_ptr("COMPACT")))
  {
    item->setCompact(BOOL::TRUE);
    lexer->nextToken();
  }
  else if (lexer->token()->name->c_str() == Token::CASCADE.name->c_str())
  {
    item->setCascade(BOOL::TRUE);
    lexer->nextToken();
  }
  else if (lexer->token()->name->c_str() == Token::CHECK.name->c_str())
  {
    item->setCheck(BOOL::TRUE);
    lexer->nextToken();
  }
}

void OracleStatementParser::parseAlterDrop(SQLAlterTableStatement_ptr stmt)
{
  lexer->nextToken();
  if (lexer->token()->name->c_str() == Token::CONSTRAINT.name->c_str())
  {
    lexer->nextToken();
    SQLAlterTableDropConstraint_ptr item = SQLAlterTableDropConstraint_ptr(new SQLAlterTableDropConstraint());
    item->setConstraintName(this->exprParser->name());
    if (lexer->token()->name->c_str() == Token::CASCADE.name->c_str())
    {
      lexer->nextToken();
      item->setCascade(BOOL::TRUE);
    }

    stmt->addItem(item);
  }
  else if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();
    SQLAlterTableDropColumnItem_ptr item = SQLAlterTableDropColumnItem_ptr(new SQLAlterTableDropColumnItem());
    this->exprParser->names(item->getColumns());
    stmt->addItem(item);
    accept(Token::RPAREN);
  }
  else if (lexer->token()->name->c_str() == Token::COLUMN.name->c_str())
  {
    lexer->nextToken();
    SQLAlterTableDropColumnItem_ptr item = SQLAlterTableDropColumnItem_ptr(new SQLAlterTableDropColumnItem());
    this->exprParser->names(item->getColumns());
    stmt->addItem(item);
  }
  else if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
  {
    lexer->nextToken();
    OracleAlterTableDropPartition_ptr item = OracleAlterTableDropPartition_ptr(new OracleAlterTableDropPartition());
    item->setName(this->exprParser->name());
    stmt->addItem(item);
  }
  else if (lexer->token()->name->c_str() == Token::INDEX.name->c_str())
  {
    lexer->nextToken();
    SQLName_ptr indexName = this->exprParser->name();
    SQLAlterTableDropIndex_ptr item = SQLAlterTableDropIndex_ptr(new SQLAlterTableDropIndex());
    item->setIndexName(indexName);
    stmt->addItem(item);
  }
  else if (lexer->token()->name->c_str() == Token::PRIMARY.name->c_str())
  {
    lexer->nextToken();
    accept(Token::KEY);
    SQLAlterTableDropPrimaryKey_ptr item = SQLAlterTableDropPrimaryKey_ptr(new SQLAlterTableDropPrimaryKey());
    stmt->addItem(item);
  }
  else
  {
    std::string tmp = "TODO : ";
    tmp += encode_util::UnicodeToUTF8(lexer->info());
    throw new ParserException(make_string_ptr(tmp));
  }
}

void OracleStatementParser::parseAlterTableSplit(SQLAlterTableStatement_ptr stmt)
{
  lexer->nextToken();
  if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
  {
    lexer->nextToken();
    OracleAlterTableSplitPartition_ptr item = OracleAlterTableSplitPartition_ptr(new OracleAlterTableSplitPartition());
    item->setName(this->exprParser->name());

    if (lexer->identifierEquals(make_string_ptr("AT")))
    {
      lexer->nextToken();
      accept(Token::LPAREN);
      this->exprParser->exprList(item->getAt(), item);
      accept(Token::RPAREN);
    }
    else
    {
      std::string tmp = "TODO : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }

    if (lexer->token()->name->c_str() == Token::INTO.name->c_str())
    {
      lexer->nextToken();
      accept(Token::LPAREN);

      for (;;)
      {
        OracleAlterTableSplitPartition_NestedTablePartitionSpec_ptr spec = OracleAlterTableSplitPartition_NestedTablePartitionSpec_ptr(new OracleAlterTableSplitPartition_NestedTablePartitionSpec());
        accept(Token::PARTITION);
        spec->setPartition(this->exprParser->name());

        for (;;)
        {
          if (lexer->token()->name->c_str() == Token::TABLESPACE.name->c_str())
          {
            lexer->nextToken();
            SQLName_ptr tablespace = this->exprParser->name();
            spec->getSegmentAttributeItems()->push_back(OracleAlterTableSplitPartition_TableSpaceItem_ptr(new OracleAlterTableSplitPartition_TableSpaceItem(tablespace)));
            continue;
          }
          else if (lexer->identifierEquals(make_string_ptr("PCTREE")))
          {
            std::string tmp = "TODO : ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
          else if (lexer->identifierEquals(make_string_ptr("PCTUSED")))
          {
            std::string tmp = "TODO : ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
          else if (lexer->identifierEquals(make_string_ptr("INITRANS")))
          {
            std::string tmp = "TODO : ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
          else if (lexer->identifierEquals(make_string_ptr("STORAGE")))
          {
            std::string tmp = "TODO : ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
          else if (lexer->identifierEquals(make_string_ptr("LOGGING")))
          {
            std::string tmp = "TODO : ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
          else if (lexer->identifierEquals(make_string_ptr("NOLOGGING")))
          {
            std::string tmp = "TODO : ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
          else if (lexer->identifierEquals(make_string_ptr("FILESYSTEM_LIKE_LOGGING")))
          {
            std::string tmp = "TODO : ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }

          break;
        }

        item->getInto()->push_back(spec);

        if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
        {
          lexer->nextToken();
          continue;
        }
        break;
      }
      accept(Token::RPAREN);
    }

    if (lexer->token()->name->c_str() == Token::UPDATE.name->c_str())
    {
      lexer->nextToken();
      acceptIdentifier("INDEXES");
      OracleAlterTableSplitPartition_UpdateIndexesClause_ptr updateIndexes = OracleAlterTableSplitPartition_UpdateIndexesClause_ptr(new OracleAlterTableSplitPartition_UpdateIndexesClause());
      item->setUpdateIndexes(updateIndexes);
    }
    stmt->addItem(item);
  }
  else
  {
    std::string tmp = "TODO : ";
    tmp += encode_util::UnicodeToUTF8(lexer->info());
    throw new ParserException(make_string_ptr(tmp));
  }
}

OracleLockTableStatement_ptr OracleStatementParser::parseLock()
{
  accept(Token::LOCK);
  accept(Token::TABLE);

  OracleLockTableStatement_ptr stmt = OracleLockTableStatement_ptr(new OracleLockTableStatement());
  stmt->setTable(this->exprParser->name());

  if (Token::PARTITION.name->c_str() == lexer->token()->name->c_str())
  {
    lexer->nextToken();
    accept(Token::LPAREN);
    stmt->setPartition(this->exprParser->expr());
    accept(Token::RPAREN);
  }

  accept(Token::IN);

  Token_ptr token = lexer->token();
  if (token->name->c_str() == Token::SHARE.name->c_str())
  {
    lexer->nextToken();

    if (lexer->token()->name->c_str() == Token::ROW.name->c_str())
    {
      lexer->nextToken();
      accept(Token::EXCLUSIVE);
      stmt->setLockMode(OracleLockTableStatement_LockMode::SHARE_ROW_EXCLUSIVE);
    }
    else if (lexer->token()->name->c_str() == Token::UPDATE.name->c_str())
    {
      lexer->nextToken();
      stmt->setLockMode(OracleLockTableStatement_LockMode::SHARE_UPDATE);
    }
    else
    {
      stmt->setLockMode(OracleLockTableStatement_LockMode::SHARE);
    }
  }
  else if (token->name->c_str() == Token::EXCLUSIVE.name->c_str())
  {
    stmt->setLockMode(OracleLockTableStatement_LockMode::EXCLUSIVE);
    lexer->nextToken();
  }
  else if (token->name->c_str() == Token::ROW.name->c_str())
  {
    lexer->nextToken();
    token = lexer->token();
    if (token->name->c_str() == Token::SHARE.name->c_str())
    {
      stmt->setLockMode(OracleLockTableStatement_LockMode::ROW_SHARE);
      lexer->nextToken();
    }
    else if (token->name->c_str() == Token::EXCLUSIVE.name->c_str())
    {
      stmt->setLockMode(OracleLockTableStatement_LockMode::ROW_EXCLUSIVE);
      lexer->nextToken();
    }
    else
    {
      throw new ParserException(make_string_ptr(encode_util::UnicodeToUTF8(lexer->info())));
    }
  }
  else
  {
    throw new ParserException(make_string_ptr(encode_util::UnicodeToUTF8(lexer->info())));
  }
  accept(Token::MODE);

  if (lexer->token()->name->c_str() == Token::NOWAIT.name->c_str())
  {
    lexer->nextToken();
    stmt->setNoWait(BOOL::TRUE);
  }
  else if (lexer->token()->name->c_str() == Token::WAIT.name->c_str())
  {
    lexer->nextToken();
    stmt->setWait(exprParser->expr());
  }
  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseBlock()
{
  SQLBlockStatement_ptr block = SQLBlockStatement_ptr(new SQLBlockStatement());
  block->setDbType(DbType_ptr(new DbType(&DbType::oracle)));

  Lexer_SavePoint_ptr savePoint_ = lexer->mark();

  if (lexer->token()->name->c_str() == Token::DECLARE.name->c_str())
  {
    lexer->nextToken();
  }

  if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str() ||
      lexer->token()->name->c_str() == Token::CURSOR.name->c_str())
  {
    parserParameters(block->getParameters(), block);
    for (auto param : *block->getParameters())
    {
      param->setParent(block);
    }
  }

  if (lexer->token()->name->c_str() == Token::PROCEDURE.name->c_str())
  {
    SQLCreateProcedureStatement_ptr stmt = this->parseCreateProcedure();
    for (auto param : *block->getParameters())
    {
      param->setParent(stmt);
      stmt->getParameters()->push_back(param);
    }
    return stmt;
  }

  if (lexer->token()->name->c_str() == Token::FUNCTION.name->c_str())
  {
    if (savePoint_->token_->name->c_str() == Token::DECLARE.name->c_str())
    {
      lexer->reset(savePoint_);
    }
    return this->parseCreateFunction();
  }

  accept(Token::BEGIN);

  parseStatementList(block->getStatementList(), -1, block);

  accept(Token::END);

  Token_ptr token = lexer->token();

  if (token->name->c_str() == Token::XEOF.name->c_str())
  {
    return block;
  }

  if (token->name->c_str() != Token::SEMI.name->c_str())
  {
    string_ptr endLabel = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    accept(Token::IDENTIFIER);
    block->setEndLabel(endLabel);
  }
  accept(Token::SEMI);

  return block;
}

void OracleStatementParser::parserParameters(SQLParameter_list_ptr parameters, SQLObject_ptr parent)
{
  for (;;)
  {
    SQLParameter_ptr parameter = SQLParameter_ptr(new SQLParameter());
    parameter->setParent(parent);

    if (instanceof <SQLObject, OracleCreateTypeStatement>(parent))
    {
      if (lexer->identifierEquals(FnvHash::Constants::MAP))
      {
        lexer->nextToken();
        parameter->setMap(BOOL::TRUE);
      }
      else if (lexer->token()->name->c_str() == Token::ORDER.name->c_str())
      {
        lexer->nextToken();
        parameter->setOrder(BOOL::TRUE);
      }

      // acceptIdentifier("MEMBER");
    }

    SQLName_ptr name;
    SQLDataType_ptr dataType = nullptr;
    if (lexer->token()->name->c_str() == Token::CURSOR.name->c_str())
    {
      lexer->nextToken();

      dataType = SQLDataTypeImpl_ptr(new SQLDataTypeImpl());
      dataType->setName(make_string_ptr("CURSOR"));

      name = this->exprParser->name();

      if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        lexer->nextToken();
        this->parserParameters(parameter->getCursorParameters(), parameter);
        accept(Token::RPAREN);
      }

      accept(Token::IS);
      SQLSelect_ptr select = this->createSQLSelectParser()->select();
      parameter->setDefaultValue(SQLQueryExpr_ptr(new SQLQueryExpr(select)));
    }
    else if (lexer->token()->name->c_str() == Token::PROCEDURE.name->c_str() || lexer->token()->name->c_str() == Token::END.name->c_str() || lexer->token()->name->c_str() == Token::TABLE.name->c_str())
    {
      break;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::TYPE))
    {
      lexer->nextToken();
      name = this->exprParser->name();
      accept(Token::IS);

      if (lexer->identifierEquals(make_string_ptr("REF")))
      {
        lexer->nextToken();
        accept(Token::CURSOR);

        dataType = SQLDataTypeImpl_ptr(new SQLDataTypeImpl(make_string_ptr("REF CURSOR")));
        dataType->setDbType(dbType);
      }
      else if (lexer->token()->name->c_str() == Token::TABLE.name->c_str())
      {
        lexer->nextToken();
        accept(Token::OF);

        name = this->exprParser->name();

        if (lexer->token()->name->c_str() == Token::PERCENT.name->c_str())
        {
          lexer->nextToken();

          string_ptr typeName;
          if (lexer->identifierEquals(FnvHash::Constants::ROWTYPE))
          {
            lexer->nextToken();
            std::string tmp = "TABLE OF ";
            tmp += name->toString()->c_str();
            tmp += "%ROWTYPE";
            typeName = make_string_ptr(tmp);
          }
          else
          {
            acceptIdentifier("TYPE");
            std::string tmp = "TABLE OF ";
            tmp += name->toString()->c_str();
            tmp += "%TYPE";
            typeName = make_string_ptr(tmp);
          }

          dataType = SQLDataTypeImpl_ptr(new SQLDataTypeImpl(typeName));
        }
        else if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
        {
          lexer->nextToken();
          string_ptr typeName = name->toString();

          SQLIntegerExpr_ptr lenExpr = std::dynamic_pointer_cast<SQLIntegerExpr>(this->exprParser->expr());
          int len = lenExpr->getNumber();
          dataType = SQLDataTypeImpl_ptr(new SQLDataTypeImpl(typeName, len));
          accept(Token::RPAREN);

          if (lexer->token()->name->c_str() == Token::INDEX.name->c_str())
          {
            lexer->nextToken();
            accept(Token::BY);
            SQLExpr_ptr indexBy = this->exprParser->primary();
            (std::dynamic_pointer_cast<SQLDataTypeImpl>(dataType))->setIndexBy(indexBy);
          }
        }
        dataType->setDbType(dbType);
      }
      else if (lexer->identifierEquals(make_string_ptr("VARRAY")))
      {
        lexer->nextToken();
        accept(Token::LPAREN);
        int len = this->exprParser->acceptInteger();
        accept(Token::RPAREN);
        accept(Token::OF);

        if (lexer->identifierEquals(make_string_ptr("NUMBER")))
        {
          lexer->nextToken();
          std::string tmp = "VARRAY(";
          tmp += std::to_string(len);
          tmp += ") OF NUMBER";
          string_ptr typeName = make_string_ptr(tmp);

          if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
          {
            accept(Token::LPAREN);
            int numLen = this->exprParser->acceptInteger();
            accept(Token::RPAREN);
            std::string tmp1 = "(";
            tmp1 += std::to_string(numLen);
            tmp1 += ")";
            typeName->append(tmp1);
          }
          dataType = SQLDataTypeImpl_ptr(new SQLDataTypeImpl(typeName));
          dataType->setDbType(dbType);
        }
        else if (lexer->identifierEquals(make_string_ptr("VARCHAR2")))
        {
          lexer->nextToken();
          std::string tmp = "VARRAY(";
          tmp += std::to_string(len);
          tmp += ") OF VARCHAR2";
          string_ptr typeName = make_string_ptr(tmp);
          dataType = SQLDataTypeImpl_ptr(new SQLDataTypeImpl(typeName));
          dataType->setDbType(dbType);

          if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
          {
            lexer->nextToken();
            this->exprParser->exprList(dataType->getArguments(), dataType);
            accept(Token::RPAREN);
          }
        }
        else
        {
          std::string tmp = "TODO : ";
          tmp += encode_util::UnicodeToUTF8(lexer->info());
          throw new ParserException(make_string_ptr(tmp));
        }
      }
      else
      {
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
    }
    else
    {
      if (lexer->token()->name->c_str() == Token::KEY.name->c_str())
      {
        name = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()))));
        lexer->nextToken();
      }
      else
      {
        name = this->exprParser->name();
      }

      if (lexer->token()->name->c_str() == Token::IN.name->c_str())
      {
        lexer->nextToken();

        if (lexer->token()->name->c_str() == Token::OUT.name->c_str())
        {
          lexer->nextToken();
          parameter->setParamType(SQLParameter_ParameterType_ptr(new SQLParameter_ParameterType(&SQLParameter_ParameterType::INOUT)));
        }
        else
        {
          parameter->setParamType(SQLParameter_ParameterType_ptr(new SQLParameter_ParameterType(&SQLParameter_ParameterType::IN)));
        }
      }
      else if (lexer->token()->name->c_str() == Token::OUT.name->c_str())
      {
        lexer->nextToken();

        if (lexer->token()->name->c_str() == Token::IN.name->c_str())
        {
          lexer->nextToken();
          parameter->setParamType(SQLParameter_ParameterType_ptr(new SQLParameter_ParameterType(&SQLParameter_ParameterType::INOUT)));
        }
        else
        {
          parameter->setParamType(SQLParameter_ParameterType_ptr(new SQLParameter_ParameterType(&SQLParameter_ParameterType::OUT)));
        }
      }
      else if (lexer->token()->name->c_str() == Token::INOUT.name->c_str())
      {
        lexer->nextToken();
        parameter->setParamType(SQLParameter_ParameterType_ptr(new SQLParameter_ParameterType(&SQLParameter_ParameterType::INOUT)));
      }

      if (lexer->identifierEquals(make_string_ptr("NOCOPY")))
      {
        lexer->nextToken();
        parameter->setNoCopy(BOOL::TRUE);
      }

      if (lexer->identifierEquals(make_string_ptr("CONSTANT")))
      {
        lexer->nextToken();
        parameter->setConstant(BOOL::TRUE);
      }

      if ((name->nameHashCode64() == FnvHash::Constants::MEMBER ||
           name->nameHashCode64() == FnvHash::Constants::STATIC) &&
          lexer->token()->name->c_str() == Token::FUNCTION.name->c_str())
      {
        if (name->nameHashCode64() == FnvHash::Constants::MEMBER)
        {
          parameter->setMember(BOOL::TRUE);
        }
        OracleFunctionDataType_ptr functionDataType = OracleFunctionDataType_ptr(new OracleFunctionDataType());
        functionDataType->setStatic((name->nameHashCode64() == FnvHash::Constants::STATIC) ? BOOL::TRUE : BOOL::FALSE);
        lexer->nextToken();
        functionDataType->setName(make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal())));
        accept(Token::IDENTIFIER);
        if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
        {
          lexer->nextToken();
          this->parserParameters(functionDataType->getParameters(), functionDataType);
          accept(Token::RPAREN);
        }
        accept(Token::RETURN);
        functionDataType->setReturnDataType(this->exprParser->parseDataType(BOOL::FALSE));
        dataType = functionDataType;
        name = nullptr;

        if (lexer->token()->name->c_str() == Token::IS.name->c_str())
        {
          lexer->nextToken();
          SQLStatement_ptr block = this->parseBlock();
          functionDataType->setBlock(block);
        }
      }
      else if ((name->nameHashCode64() == FnvHash::Constants::MEMBER ||
                name->nameHashCode64() == FnvHash::Constants::STATIC) &&
               lexer->token()->name->c_str() == Token::PROCEDURE.name->c_str())
      {
        if (name->nameHashCode64() == FnvHash::Constants::MEMBER)
        {
          parameter->setMember(BOOL::TRUE);
        }
        OracleProcedureDataType_ptr procedureDataType = OracleProcedureDataType_ptr(new OracleProcedureDataType());
        procedureDataType->setStatic((name->nameHashCode64() == FnvHash::Constants::STATIC) ? BOOL::TRUE : BOOL::FALSE);
        lexer->nextToken();
        procedureDataType->setName(make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal())));
        accept(Token::IDENTIFIER);
        if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
        {
          lexer->nextToken();
          this->parserParameters(procedureDataType->getParameters(), procedureDataType);
          accept(Token::RPAREN);
        }

        dataType = procedureDataType;
        name = nullptr;

        if (lexer->token()->name->c_str() == Token::IS.name->c_str())
        {
          lexer->nextToken();
          SQLStatement_ptr block = this->parseBlock();
          procedureDataType->setBlock(block);
        }
      }
      else
      {
        dataType = this->exprParser->parseDataType(BOOL::FALSE);
      }
      if (lexer->token()->name->c_str() == Token::COLONEQ.name->c_str() ||
          lexer->token()->name->c_str() == Token::DEFAULT.name->c_str())
      {
        lexer->nextToken();
        parameter->setDefaultValue(this->exprParser->expr());
      }
    }

    parameter->setName(name);
    parameter->setDataType(dataType);

    parameters->push_back(parameter);
    Token_ptr token = lexer->token();
    if (token->name->c_str() == Token::COMMA.name->c_str() ||
        token->name->c_str() == Token::SEMI.name->c_str() ||
        token->name->c_str() == Token::IS.name->c_str())
    {
      lexer->nextToken();
    }

    token = lexer->token();
    if (token->name->c_str() != Token::BEGIN.name->c_str() && token->name->c_str() != Token::RPAREN.name->c_str() && token->name->c_str() != Token::XEOF.name->c_str() && token->name->c_str() != Token::FUNCTION.name->c_str() && !lexer->identifierEquals(make_string_ptr("DETERMINISTIC")))
    {
      continue;
    }

    break;
  }
}

OracleSelectParser_ptr OracleStatementParser::createSQLSelectParser()
{
  return OracleSelectParser_ptr(new OracleSelectParser(this->exprParser, selectListCache));
}

SQLStatement_ptr OracleStatementParser::parseInsert()
{
  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    OracleInsertStatement_ptr stmt = OracleInsertStatement_ptr(new OracleInsertStatement());
    parseInsert0(stmt, BOOL::FALSE);

    stmt->setReturning(parseReturningClause());
    stmt->setErrorLogging(parseErrorLoggingClause());

    return stmt;
  }

  accept(Token::INSERT);

  SQLHint_list_ptr hints = std::make_shared<std::list<SQLHint_ptr>>();

  parseHints(hints);

  if (lexer->token()->name->c_str() == Token::INTO.name->c_str())
  {
    OracleInsertStatement_ptr stmt = OracleInsertStatement_ptr(new OracleInsertStatement());
    stmt->setHints(hints);

    parseInsert0(stmt);

    stmt->setReturning(parseReturningClause());
    stmt->setErrorLogging(parseErrorLoggingClause());

    return stmt;
  }

  OracleMultiInsertStatement_ptr stmt = parseMultiInsert();
  stmt->setHints(hints);
  return stmt;
}

OracleMultiInsertStatement_ptr OracleStatementParser::parseMultiInsert()
{
  OracleMultiInsertStatement_ptr stmt = OracleMultiInsertStatement_ptr(new OracleMultiInsertStatement());

  if (lexer->token()->name->c_str() == Token::ALL.name->c_str())
  {
    lexer->nextToken();
    stmt->setOption(OracleMultiInsertStatement_Option::ALL);
  }
  else if (lexer->token()->name->c_str() == Token::FIRST.name->c_str() ||
           lexer->identifierEquals(make_string_ptr("FIRST")))
  {
    lexer->nextToken();
    stmt->setOption(OracleMultiInsertStatement_Option::FIRST);
  }

  while (lexer->token()->name->c_str() == Token::INTO.name->c_str())
  {
    OracleMultiInsertStatement_InsertIntoClause_ptr clause = OracleMultiInsertStatement_InsertIntoClause_ptr(new OracleMultiInsertStatement_InsertIntoClause());

    BOOL_ptr acceptSubQuery = stmt->getEntries()->empty() ? BOOL::TRUE : BOOL::FALSE;
    parseInsert0(clause, acceptSubQuery);

    clause->setReturning(parseReturningClause());
    clause->setErrorLogging(parseErrorLoggingClause());

    stmt->addEntry(clause);
  }

  if (lexer->token()->name->c_str() == Token::WHEN.name->c_str())
  {
    OracleMultiInsertStatement_ConditionalInsertClause_ptr clause = OracleMultiInsertStatement_ConditionalInsertClause_ptr(new OracleMultiInsertStatement_ConditionalInsertClause());

    while (lexer->token()->name->c_str() == Token::WHEN.name->c_str())
    {
      lexer->nextToken();

      OracleMultiInsertStatement_ConditionalInsertClauseItem_ptr item = OracleMultiInsertStatement_ConditionalInsertClauseItem_ptr(new OracleMultiInsertStatement_ConditionalInsertClauseItem());

      item->setWhen(this->exprParser->expr());
      accept(Token::THEN);
      OracleMultiInsertStatement_InsertIntoClause_ptr insertInto = OracleMultiInsertStatement_InsertIntoClause_ptr(new OracleMultiInsertStatement_InsertIntoClause());
      parseInsert0(insertInto);
      item->setThen(insertInto);

      clause->addItem(item);
    }

    if (lexer->token()->name->c_str() == Token::ELSE.name->c_str())
    {
      lexer->nextToken();

      OracleMultiInsertStatement_InsertIntoClause_ptr insertInto = OracleMultiInsertStatement_InsertIntoClause_ptr(new OracleMultiInsertStatement_InsertIntoClause());
      parseInsert0(insertInto, BOOL::FALSE);
      clause->setElseItem(insertInto);
    }
    stmt->addEntry(clause);
  }

  SQLSelect_ptr subQuery = this->createSQLSelectParser()->select();
  stmt->setSubQuery(subQuery);

  return stmt;
}

OracleExceptionStatement_ptr OracleStatementParser::parseException()
{
  accept(Token::EXCEPTION);
  OracleExceptionStatement_ptr stmt = OracleExceptionStatement_ptr(new OracleExceptionStatement());

  for (;;)
  {
    accept(Token::WHEN);
    OracleExceptionStatement_Item_ptr item = OracleExceptionStatement_Item_ptr(new OracleExceptionStatement_Item());
    item->setWhen(this->exprParser->expr());
    accept(Token::THEN);

    this->parseStatementList(item->getStatements(), -1, item);

    stmt->addItem(item);

    if (lexer->token()->name->c_str() == Token::SEMI.name->c_str())
    {
      lexer->nextToken();
    }

    if (lexer->token()->name->c_str() != Token::WHEN.name->c_str())
    {
      break;
    }
  }
  return stmt;
}

OracleReturningClause_ptr OracleStatementParser::parseReturningClause()
{
  OracleReturningClause_ptr clause = nullptr;

  if (lexer->token()->name->c_str() == Token::RETURNING.name->c_str())
  {
    lexer->nextToken();
    clause = OracleReturningClause_ptr(new OracleReturningClause());

    for (;;)
    {
      SQLExpr_ptr item = exprParser->expr();
      clause->addItem(item);
      if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
      {
        lexer->nextToken();
        continue;
      }
      break;
    }
    accept(Token::INTO);
    for (;;)
    {
      SQLExpr_ptr item = exprParser->expr();
      clause->addValue(item);
      if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
      {
        lexer->nextToken();
        continue;
      }
      break;
    }
  }
  return clause;
}

SQLExplainStatement_ptr OracleStatementParser::parseExplain()
{
  accept(Token::EXPLAIN);
  acceptIdentifier("PLAN");
  OracleExplainStatement_ptr stmt = OracleExplainStatement_ptr(new OracleExplainStatement());

  if (lexer->token()->name->c_str() == Token::SET.name->c_str())
  {
    lexer->nextToken();
    acceptIdentifier("STATEMENT_ID");
    accept(Token::EQ);
    stmt->setStatementId(this->exprParser->primary());
  }

  if (lexer->token()->name->c_str() == Token::INTO.name->c_str())
  {
    lexer->nextToken();
    stmt->setInto(this->exprParser->name());
  }

  accept(Token::FOR);
  stmt->setStatement(parseStatement());

  return stmt;
}

SQLAnalyzeTableStatement_ptr OracleStatementParser::parseAnalyzeTable()
{
  accept(Token::TABLE);
  SQLAnalyzeTableStatement_ptr stmt = SQLAnalyzeTableStatement_ptr(new SQLAnalyzeTableStatement());

  SQLName_list_ptr names = std::make_shared<std::list<SQLName_ptr>>();
  this->exprParser->names(names, stmt);

  for (SQLName_ptr name : *names)
  {
    stmt->setTable(SQLExprTableSource_ptr(new SQLExprTableSource(name)));
  }

  if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
  {
    stmt->setPartition(parsePartitionRef());
  }

  if (lexer->token()->name->c_str() == Token::COMPUTE.name->c_str())
  {
    lexer->nextToken();
    acceptIdentifier("STATISTICS");
    stmt->setComputeStatistics(BOOL::TRUE);
  }

  return stmt;
}

SQLDeleteStatement_ptr OracleStatementParser::parseDeleteStatement()
{
  OracleDeleteStatement_ptr deleteStatement = OracleDeleteStatement_ptr(new OracleDeleteStatement());

  if (lexer->token()->name->c_str() == Token::DELETE.name->c_str())
  {
    lexer->nextToken();

    if (lexer->token()->name->c_str() == Token::COMMENT.name->c_str())
    {
      lexer->nextToken();
    }

    parseHints(deleteStatement->getHints());

    if (lexer->token()->name->c_str() == (Token::FROM.name->c_str()))
    {
      lexer->nextToken();
    }

    if (lexer->identifierEquals(make_string_ptr("ONLY")))
    {
      lexer->nextToken();
      accept(Token::LPAREN);

      SQLName_ptr tableName = exprParser->name();
      deleteStatement->setTableName(tableName);

      accept(Token::RPAREN);
    }
    else if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      SQLTableSource_ptr tableSource = this->createSQLSelectParser()->parseTableSource();
      deleteStatement->setTableSource(tableSource);
    }
    else
    {
      SQLName_ptr tableName = exprParser->name();
      deleteStatement->setTableName(tableName);
    }

    deleteStatement->setAlias(tableAlias());
  }

  if (lexer->token()->name->c_str() == (Token::WHERE.name->c_str()))
  {
    lexer->nextToken();
    deleteStatement->setWhere(this->exprParser->expr());
  }

  if (lexer->token()->name->c_str() == Token::RETURNING.name->c_str())
  {
    OracleReturningClause_ptr clause = this->parseReturningClause();
    deleteStatement->setReturning(clause);
  }
  if (lexer->identifierEquals(make_string_ptr("RETURN")) ||
      lexer->identifierEquals(make_string_ptr("RETURNING")))
  {
    std::string tmp = "TODO. ";
    tmp += encode_util::UnicodeToUTF8(lexer->info());
    throw new ParserException(make_string_ptr(tmp));
  }

  if (lexer->identifierEquals(make_string_ptr("LOG")))
  {
    std::string tmp = "TODO. ";
    tmp += encode_util::UnicodeToUTF8(lexer->info());
    throw new ParserException(make_string_ptr(tmp));
  }

  return deleteStatement;
}

SQLStatement_ptr OracleStatementParser::parseCreateDbLink()
{
  accept(Token::CREATE);

  OracleCreateDatabaseDbLinkStatement_ptr dbLink = OracleCreateDatabaseDbLinkStatement_ptr(new OracleCreateDatabaseDbLinkStatement());

  if (lexer->identifierEquals(make_string_ptr("SHARED")))
  {
    dbLink->setShared(BOOL::TRUE);
    lexer->nextToken();
  }

  if (lexer->identifierEquals(make_string_ptr("PUBLIC")))
  {
    dbLink->setPublic(BOOL::TRUE);
    lexer->nextToken();
  }

  accept(Token::DATABASE);
  acceptIdentifier("LINK");

  dbLink->setName(this->exprParser->name());

  if (lexer->token()->name->c_str() == Token::CONNECT.name->c_str())
  {
    lexer->nextToken();
    accept(Token::TO);

    dbLink->setUser(this->exprParser->name());

    if (lexer->identifierEquals(FnvHash::Constants::IDENTIFIED))
    {
      lexer->nextToken();
      accept(Token::BY);
      dbLink->setPassword(make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal())));

      if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
      {
        lexer->nextToken();
      }
      else
      {
        accept(Token::LITERAL_ALIAS);
      }
    }
  }

  if (lexer->identifierEquals(make_string_ptr("AUTHENTICATED")))
  {
    lexer->nextToken();
    accept(Token::BY);
    dbLink->setAuthenticatedUser(this->exprParser->name());

    acceptIdentifier("IDENTIFIED");
    accept(Token::BY);
    dbLink->setPassword(make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal())));
    accept(Token::IDENTIFIER);
  }

  if (lexer->token()->name->c_str() == Token::USING.name->c_str())
  {
    lexer->nextToken();
    dbLink->setUsing(this->exprParser->expr());
  }

  return dbLink;
}

SQLStatement_ptr OracleStatementParser::parseCreateIndex(BOOL_ptr acceptCreate)
{
  if (acceptCreate)
  {
    accept(Token::CREATE);
  }

  OracleCreateIndexStatement_ptr stmt = OracleCreateIndexStatement_ptr(new OracleCreateIndexStatement());
  if (lexer->token()->name->c_str() == Token::UNIQUE.name->c_str())
  {
    stmt->setType(make_string_ptr("UNIQUE"));
    lexer->nextToken();
  }
  else if (lexer->identifierEquals(make_string_ptr("BITMAP")))
  {
    stmt->setType(make_string_ptr("BITMAP"));
    lexer->nextToken();
  }

  accept(Token::INDEX);

  stmt->setName(this->exprParser->name());

  accept(Token::ON);

  if (lexer->identifierEquals(make_string_ptr("CLUSTER")))
  {
    lexer->nextToken();
    stmt->setCluster(BOOL::TRUE);
  }

  stmt->setTable(this->exprParser->name());

  if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
  {
    string_ptr alias = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    stmt->getTable()->setAlias(alias);
    lexer->nextToken();
  }

  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();

    for (;;)
    {
      SQLSelectOrderByItem_ptr item = this->exprParser->parseSelectOrderByItem();
      stmt->addItem(item);
      if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
      {
        lexer->nextToken();
        continue;
      }
      break;
    }
    accept(Token::RPAREN);
  }

  for (;;)
  {
    this->getExprParser()->parseSegmentAttributes(stmt);

    if (lexer->token()->name->c_str() == Token::COMPUTE.name->c_str())
    {
      lexer->nextToken();
      acceptIdentifier("STATISTICS");
      stmt->setComputeStatistics(BOOL::TRUE);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
    {
      lexer->nextToken();
      stmt->setEnable(BOOL::TRUE);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
    {
      lexer->nextToken();
      stmt->setEnable(BOOL::FALSE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("ONLINE")))
    {
      lexer->nextToken();
      stmt->setOnline(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("NOPARALLEL")))
    {
      lexer->nextToken();
      stmt->setNoParallel(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("PARALLEL")))
    {
      lexer->nextToken();
      stmt->setParallel(this->exprParser->expr());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::INDEX.name->c_str())
    {
      lexer->nextToken();
      acceptIdentifier("ONLY");
      acceptIdentifier("TOPLEVEL");
      stmt->setIndexOnlyTopLevel(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("SORT")))
    {
      lexer->nextToken();
      stmt->setSort(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("NOSORT")))
    {
      lexer->nextToken();
      stmt->setSort(BOOL::FALSE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("REVERSE")))
    {
      lexer->nextToken();
      stmt->setReverse(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("LOCAL")))
    {
      lexer->nextToken();
      stmt->setLocal(BOOL::TRUE);

      for (;;)
      {
        if (lexer->token()->name->c_str() == Token::STORE.name->c_str())
        {
          lexer->nextToken();
          accept(Token::IN);
          accept(Token::LPAREN);
          this->exprParser->names(stmt->getLocalStoreIn(), stmt);
          accept(Token::RPAREN);
        }
        else if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
        {
          lexer->nextToken();
          for (;;)
          {
            SQLPartition_ptr partition = this->getExprParser()->parsePartition();
            partition->setParent(stmt);
            stmt->getLocalPartitions()->push_back(partition);
            if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
            {
              lexer->nextToken();
              continue;
            }
            else if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
            {
              lexer->nextToken();
              break;
            }
            std::string tmp = "TODO : ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
        }
        else
        {
          break;
        }
      }
    }
    else if (lexer->identifierEquals(make_string_ptr("GLOBAL")))
    {
      lexer->nextToken();
      stmt->setGlobal(BOOL::TRUE);

      if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
      {
        lexer->nextToken();

        accept(Token::BY);

        if (lexer->identifierEquals(make_string_ptr("RANGE")))
        {
          SQLPartitionByRange_ptr partitionByRange = this->getExprParser()->partitionByRange();
          this->getExprParser()->partitionClauseRest(partitionByRange);
          partitionByRange->setParent(stmt);
          stmt->getGlobalPartitions()->push_back(partitionByRange);
          continue;
        }
        else if (lexer->identifierEquals(make_string_ptr("HASH")))
        {
          SQLPartitionByHash_ptr partitionByHash = this->getExprParser()->partitionByHash();
          this->getExprParser()->partitionClauseRest(partitionByHash);

          if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
          {
            lexer->nextToken();
            for (;;)
            {
              SQLPartition_ptr partition = this->getExprParser()->parsePartition();
              partitionByHash->addPartition(partition);
              if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
              {
                lexer->nextToken();
                continue;
              }
              else if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
              {
                lexer->nextToken();
                break;
              }
              std::string tmp = "TODO : ";
              tmp += encode_util::UnicodeToUTF8(lexer->info());
              throw new ParserException(make_string_ptr(tmp));
            }
          }
          partitionByHash->setParent(stmt);
          stmt->getGlobalPartitions()->push_back(partitionByHash);
          continue;
        }
      }

      break;
    }
    else
    {
      break;
    }
  }
  return stmt;
}

SQLCreateSequenceStatement_ptr OracleStatementParser::parseCreateSequence(BOOL_ptr acceptCreate)
{
  if (acceptCreate)
  {
    accept(Token::CREATE);
  }

  accept(Token::SEQUENCE);

  SQLCreateSequenceStatement_ptr stmt = SQLCreateSequenceStatement_ptr(new SQLCreateSequenceStatement());
  stmt->setDbType(DbType_ptr(new DbType(&DbType::oracle)));
  stmt->setName(this->exprParser->name());

  for (;;)
  {
    if (lexer->token()->name->c_str() == Token::START.name->c_str())
    {
      lexer->nextToken();
      accept(Token::WITH);
      stmt->setStartWith(this->exprParser->expr());
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("INCREMENT")))
    {
      lexer->nextToken();
      accept(Token::BY);
      stmt->setIncrementBy(this->exprParser->expr());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::CACHE.name->c_str())
    {
      lexer->nextToken();
      stmt->setCache(BOOL::TRUE);

      if (lexer->token()->name->c_str() == Token::LITERAL_INT.name->c_str() ||
          lexer->token()->name->c_str() == Token::QUES.name->c_str())
      {
        stmt->setCacheValue(this->exprParser->primary());
      }

      continue;
    }
    else if (lexer->token()->name->c_str() == Token::NOCACHE.name->c_str())
    {
      lexer->nextToken();
      stmt->setCache(BOOL::FALSE);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::ORDER.name->c_str())
    {
      lexer->nextToken();
      stmt->setOrder(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("NOORDER")))
    {
      lexer->nextToken();
      stmt->setOrder(BOOL::FALSE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("CYCLE")))
    {
      lexer->nextToken();
      stmt->setCycle(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("NOCYCLE")))
    {
      lexer->nextToken();
      stmt->setCycle(BOOL::FALSE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("MINVALUE")))
    {
      lexer->nextToken();
      stmt->setMinValue(this->exprParser->expr());
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("MAXVALUE")))
    {
      lexer->nextToken();
      stmt->setMaxValue(this->exprParser->expr());
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("NOMAXVALUE")))
    {
      lexer->nextToken();
      stmt->setNoMaxValue(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("NOMINVALUE")))
    {
      lexer->nextToken();
      stmt->setNoMinValue(BOOL::TRUE);
      continue;
    }
    break;
  }

  return stmt;
}

SQLCreateProcedureStatement_ptr OracleStatementParser::parseCreateProcedure()
{
  SQLCreateProcedureStatement_ptr stmt = SQLCreateProcedureStatement_ptr(new SQLCreateProcedureStatement());
  stmt->setDbType(dbType);

  if (lexer->token()->name->c_str() == Token::CREATE.name->c_str())
  {
    lexer->nextToken();
    if (lexer->token()->name->c_str() == Token::OR.name->c_str())
    {
      lexer->nextToken();
      accept(Token::REPLACE);
      stmt->setOrReplace(BOOL::TRUE);
    }
  }
  else
  {
    stmt->setCreate(BOOL::FALSE);
  }

  accept(Token::PROCEDURE);

  SQLName_ptr procedureName = this->exprParser->name();
  stmt->setName(procedureName);

  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();
    parserParameters(stmt->getParameters(), stmt);
    accept(Token::RPAREN);
  }

  if (lexer->identifierEquals(make_string_ptr("AUTHID")))
  {
    lexer->nextToken();
    string_ptr strVal = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    if (lexer->identifierEquals(make_string_ptr("CURRENT_USER")))
    {
      lexer->nextToken();
    }
    else
    {
      acceptIdentifier("DEFINER");
    }
    SQLName_ptr authid = SQLName_ptr(new SQLIdentifierExpr(strVal));
    stmt->setAuthid(authid);
  }

  if (lexer->identifierEquals(FnvHash::Constants::WRAPPED))
  {
    lexer->nextToken();
    int pos = lexer->text.find(';', lexer->pos());
    if (pos != std::wstring::npos)
    {
      string_ptr wrappedString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->subString(lexer->pos(), pos - lexer->pos())));
      stmt->setWrappedSource(wrappedString);
      lexer->reset(pos, ';', Token_ptr(new Token(&Token::LITERAL_CHARS)));
      lexer->nextToken();
      stmt->setAfterSemi(BOOL::TRUE);
    }
    else
    {
      string_ptr wrappedString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->subString(lexer->pos())));
      stmt->setWrappedSource(wrappedString);
      lexer->reset(lexer->text.length(), (char)LayoutCharacters::EOI, Token_ptr(new Token(&Token::XEOF)));
    }
    return stmt;
  }

  if (lexer->token()->name->c_str() == Token::SEMI.name->c_str())
  {
    lexer->nextToken();
    return stmt;
  }

  if (lexer->token()->name->c_str() == Token::IS.name->c_str())
  {
    lexer->nextToken();
  }
  else
  {
    accept(Token::AS);
  }

  if (lexer->identifierEquals(make_string_ptr("LANGUAGE")))
  {
    lexer->nextToken();
    if (lexer->identifierEquals(make_string_ptr("JAVA")))
    {
      lexer->nextToken();
      acceptIdentifier("NAME");
      string_ptr javaCallSpec = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      accept(Token::LITERAL_CHARS);
      stmt->setJavaCallSpec(javaCallSpec);
    }
    else
    {
      std::string tmp = "TODO : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
    return stmt;
  }

  SQLStatement_ptr block = this->parseBlock();

  stmt->setBlock(block);

  if (lexer->identifierEquals(procedureName->getSimpleName()))
  {
    lexer->nextToken();
  }

  return stmt;
}

SQLUpdateStatement_ptr OracleStatementParser::parseUpdateStatement()
{
  return (new OracleUpdateParser(this->lexer))->parseUpdateStatement();
}

SQLStatement_ptr OracleStatementParser::parseCreatePackage()
{
  accept(Token::CREATE);

  BOOL_ptr repalce = BOOL::FALSE;
  if (lexer->token()->name->c_str() == Token::OR.name->c_str())
  {
    lexer->nextToken();
    accept(Token::REPLACE);
    repalce = BOOL::TRUE;
  }

  acceptIdentifier("PACKAGE");

  OracleCreatePackageStatement_ptr stmt = OracleCreatePackageStatement_ptr(new OracleCreatePackageStatement());
  stmt->setOrReplace(repalce);

  if (lexer->identifierEquals(make_string_ptr("BODY")))
  {
    lexer->nextToken();
    stmt->setBody(BOOL::TRUE);
  }

  SQLName_ptr pkgName = this->exprParser->name();
  stmt->setName(pkgName);

  if (lexer->token()->name->c_str() == Token::IS.name->c_str())
  {
    lexer->nextToken();
  }
  else
  {
    accept(Token::AS);
  }

  // this->parseStatementList(stmt->getStatements(), -1, stmt);
  for (;;)
  {
    if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
    {
      SQLDeclareStatement_ptr varDecl = SQLDeclareStatement_ptr(new SQLDeclareStatement());
      varDecl->setDbType(dbType);
      varDecl->setParent(stmt);

      SQLDeclareItem_ptr varItem = SQLDeclareItem_ptr(new SQLDeclareItem());

      BOOL_ptr type = BOOL::FALSE;
      if (lexer->identifierEquals(FnvHash::Constants::TYPE))
      {
        lexer->nextToken();
        type = BOOL::TRUE;
      }

      SQLName_ptr name = this->exprParser->name();
      varItem->setName(name);

      if (type)
      {
        accept(Token::IS);
        if (lexer->identifierEquals(FnvHash::Constants::RECORD))
        {
          lexer->nextToken();

          SQLRecordDataType_ptr recordDataType = SQLRecordDataType_ptr(new SQLRecordDataType());

          accept(Token::LPAREN);
          for (;;)
          {
            SQLColumnDefinition_ptr column = this->exprParser->parseColumn();
            recordDataType->addColumn(column);
            if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
            {
              lexer->nextToken();
              continue;
            }
            break;
          }
          accept(Token::RPAREN);
          varItem->setDataType(recordDataType);
        }
        else
        {
          acceptIdentifier("REF");
          accept(Token::CURSOR);
          varItem->setDataType(SQLDataTypeImpl_ptr(new SQLDataTypeImpl(make_string_ptr("REF CURSOR"))));
        }
      }
      else
      {
        varItem->setDataType(this->exprParser->parseDataType(BOOL::FALSE));
      }
      varItem->setParent(varDecl);

      if (lexer->token()->name->c_str() == Token::COLONEQ.name->c_str())
      {
        lexer->nextToken();
        SQLExpr_ptr defaultVal = this->exprParser->expr();
        varItem->setValue(defaultVal);
      }

      varDecl->getItems()->push_back(varItem);

      accept(Token::SEMI);
      varDecl->setAfterSemi(BOOL::TRUE);

      stmt->getStatements()->push_back(varDecl);
    }
    else if (lexer->token()->name->c_str() == Token::FUNCTION.name->c_str())
    {
      SQLStatement_ptr function = this->parseFunction();
      function->setParent(stmt);
      stmt->getStatements()->push_back(function);
    }
    else if (lexer->token()->name->c_str() == Token::PROCEDURE.name->c_str())
    {
      SQLStatement_ptr proc = this->parseCreateProcedure();
      proc->setParent(stmt);
      stmt->getStatements()->push_back(proc);
    }
    else if (lexer->token()->name->c_str() == Token::END.name->c_str())
    {
      break;
    }
    else if (lexer->token()->name->c_str() == Token::BEGIN.name->c_str())
    {
      lexer->nextToken();
      SQLBlockStatement_ptr block = SQLBlockStatement_ptr(new SQLBlockStatement());
      parseStatementList(block->getStatementList(), -1, block);
      accept(Token::END);
      block->setParent(stmt);
      stmt->getStatements()->push_back(block);

      if (lexer->identifierEquals(pkgName->getSimpleName()))
      {
        lexer->nextToken();
        accept(Token::SEMI);
        return stmt;
      }

      break;
    }
    else
    {
      std::string tmp = "TODO : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
  }

  accept(Token::END);

  if (lexer->identifierEquals(pkgName->getSimpleName()))
  {
    lexer->nextToken();
  }

  accept(Token::SEMI);
  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseCreateSynonym()
{
  OracleCreateSynonymStatement_ptr stmt = OracleCreateSynonymStatement_ptr(new OracleCreateSynonymStatement());
  accept(Token::CREATE);

  if (lexer->token()->name->c_str() == Token::OR.name->c_str())
  {
    lexer->nextToken();
    accept(Token::REPLACE);
    stmt->setOrReplace(BOOL::TRUE);
  }

  if (lexer->identifierEquals(make_string_ptr("PUBLIC")))
  {
    lexer->nextToken();
    stmt->setPublic(BOOL::TRUE);
  }

  acceptIdentifier("SYNONYM");

  stmt->setName(this->exprParser->name());

  accept(Token::FOR);

  stmt->setObject(this->exprParser->name());
  return stmt;
}

SQLStatement_ptr OracleStatementParser::parseCreateType()
{
  OracleCreateTypeStatement_ptr stmt = OracleCreateTypeStatement_ptr(new OracleCreateTypeStatement());
  accept(Token::CREATE);

  if (lexer->token()->name->c_str() == Token::OR.name->c_str())
  {
    lexer->nextToken();
    accept(Token::REPLACE);
    stmt->setOrReplace(BOOL::TRUE);
  }

  acceptIdentifier("TYPE");

  if (lexer->identifierEquals(make_string_ptr("BODY")))
  {
    lexer->nextToken();
    stmt->setBody(BOOL::TRUE);
  }

  SQLName_ptr name = this->exprParser->name();
  stmt->setName(name);

  if (lexer->identifierEquals(FnvHash::Constants::UNDER))
  {
    lexer->nextToken();
    SQLName_ptr under = this->exprParser->name();
    stmt->setUnder(under);
  }

  if (lexer->identifierEquals(FnvHash::Constants::AUTHID))
  {
    lexer->nextToken();
    SQLName_ptr authId = this->exprParser->name();
    stmt->setAuthId(authId);
  }

  if (lexer->token()->name->c_str() == Token::AS.name->c_str() ||
      lexer->token()->name->c_str() == Token::IS.name->c_str())
  {
    lexer->nextToken();
  }

  if (lexer->identifierEquals(make_string_ptr("OBJECT")))
  {
    lexer->nextToken();
    stmt->setObject(BOOL::TRUE);
  }

  if (lexer->identifierEquals(FnvHash::Constants::STATIC))
  {
    this->parserParameters(stmt->getParameters(), stmt);
  }
  else if (lexer->token()->name->c_str() == Token::TABLE.name->c_str())
  {
    lexer->nextToken();
    accept(Token::OF);
    SQLDataType_ptr dataType = this->exprParser->parseDataType();
    stmt->setTableOf(dataType);
  }
  else if (lexer->identifierEquals(FnvHash::Constants::VARRAY))
  {
    lexer->nextToken();
    accept(Token::LPAREN);
    SQLExpr_ptr sizeLimit = this->exprParser->primary();
    stmt->setVarraySizeLimit(sizeLimit);
    accept(Token::RPAREN);

    accept(Token::OF);
    SQLDataType_ptr dataType = this->exprParser->parseDataType();
    stmt->setVarrayDataType(dataType);
  }
  else if (lexer->identifierEquals(FnvHash::Constants::WRAPPED))
  {
    int pos = lexer->text.find(';', lexer->pos());
    if (pos != std::wstring::npos)
    {
      string_ptr wrappedString = make_string_ptr(encode_util::UnicodeToUTF8(lexer->subString(lexer->pos(), pos - lexer->pos())));
      stmt->setWrappedSource(wrappedString);
      lexer->reset(pos, ';', Token_ptr(new Token(&Token::LITERAL_CHARS)));
      lexer->nextToken();
    }
  }
  else
  {
    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      this->parserParameters(stmt->getParameters(), stmt);
      stmt->setParen(BOOL::TRUE);
      accept(Token::RPAREN);
    }
    else
    {
      this->parserParameters(stmt->getParameters(), stmt);
      if (lexer->token()->name->c_str() == Token::END.name->c_str())
      {
        lexer->nextToken();
      }
    }
  }

  for (;;)
  {
    if (lexer->token()->name->c_str() == Token::NOT.name->c_str())
    {
      lexer->nextToken();
      if (lexer->identifierEquals(FnvHash::Constants::FINAL))
      {
        lexer->nextToken();
        stmt->setFinal(BOOL::FALSE);
      }
      else
      {
        acceptIdentifier("INSTANTIABLE");
        stmt->setInstantiable(BOOL::FALSE);
      }
    }
    else if (lexer->identifierEquals(FnvHash::Constants::FINAL))
    {
      lexer->nextToken();
      stmt->setFinal(BOOL::TRUE);
    }
    else if (lexer->identifierEquals(FnvHash::Constants::INSTANTIABLE))
    {
      lexer->nextToken();
      stmt->setInstantiable(BOOL::TRUE);
    }
    else
    {
      break;
    }
  }

  if (lexer->token()->name->c_str() == Token::SEMI.name->c_str())
  {
    lexer->nextToken();
    stmt->setAfterSemi(BOOL::TRUE);
  }
  return stmt;
}
